专栏名称: 石杉的架构笔记
专注原创、用心雕琢!十余年BAT一线大厂架构经验倾囊相授
目录
相关文章推荐
红古发布  ·  改善睡眠的方法来了:晚餐吃点它…… ·  昨天  
LRTV辽宁之声  ·  刷短视频“刷”成2300度近视?这些习惯,正 ... ·  2 天前  
镇江发布  ·  提醒家人!这些食物可常吃! ·  2 天前  
南京日报  ·  正大量上市,有人吃进急诊! ·  2 天前  
51好读  ›  专栏  ›  石杉的架构笔记

30道“热乎乎”的 JVM 典型题目剖析!

石杉的架构笔记  · 公众号  ·  · 2019-07-08 08:28

正文

公众号后台回复“ 学习 ”,获取作者独家秘制精品资料


多年好友心血力作, 阿里资深技术专家 十余年JVM生产实践经验

《从 开始带你成为 JVM 实战 高手》

限时优惠: 88元 正在进行ing

专栏目录 参见文末

扫下方海报进行 试读

通过我的海报购买, 再返你24元

领取方式: 加微信号:Giotto1245 ,暗号: 返现



写在前面

大家好,我是片头专栏《从零开始带你成为JVM实战高手》的作者,外号救火队队长,目前在阿里任职。

这个专栏已经写作一周了,也在好友石杉老哥公众号这里宣传霸屏一周了,在此感谢老哥们儿的大力推荐,有空来杭州一定请你喝两杯^_^

好了,言归正传,从第一周读者的反响来说,整体很好,因为定位是从零开始,我用了大量的手绘彩图进行讲解,这点深得大家赞同。

不过,也有不少“老鸟”同学希望快一些,尽快到达后面的精华部分: 案例实战 。不过我还是想提一句,因为照顾到很多JVM基础薄弱甚至没有基础的同学,所以还是会稳扎稳打,希望这些有基础的同学,权当复习一遍吧!

另外,第一周的文章,大家的互动也很活跃。看到这些我非常欣慰,如果光是我写,你不思考,那效果会大打折扣。

下面,我整理了第一周所有同学问题中最典型的30个问题,并给出了解答,大家可以仔细看看,认真思考,看是不是你也对这些问题曾经有过疑惑?

问题一



问题
方法走完,引用消失,堆内存还未必消失。 好多人在做报表导出的时候,就会在for循环里不断的创建对象,很容易造成堆溢出,请问这种大文件导出怎么破?
解答
建议不要在for里创建对象,可以在外面搞一个对象,for循环里对一个对象修改数据即可


问题二




问题
1.J ava支持多线程,每个线程有自己的Java虚拟机栈和本地方法栈,是这样吗?

2.新建的实例在堆内存,实例变量也是在堆内存? 是这样吗?

解答
1、2两点均理解正确


问题三



问题
您好,我不太看懂入栈和出栈有什么意义,可以给我解释一下吗?谢谢!
解答

入栈的时候,就是你执行一个方法的时候,为这个方法创建一个栈帧入栈

出栈,就是你的方法执行完毕了,就会出栈,其实这个不用急,明天的文章会有详细的图解,你会看明白的。



问题四



问题
如果是父类子类的情况是下面哪种呢? 加载父类->加载子类->初始化父类->初始化子类, 加载父类->初始化父类->加载子类->初始化子类
解答
不是的,加载父类就是父类,除非用到子类才会加载子类;但是加载子类要初始化之前,必须先加载父类,初始化父类



问题五



问题

类加载器有三层,如果在第二层的类加载器可以加载这些类的话,就没有必要往上去找他的父类加载吗?

既然说类只有用到的时候才加载到内存中,那么new对象的时候肯定用到,但是是不是先经历过类的所有过程才将类实例化?

解答
没错,必须先加载类,再实例化对象


问题六


问题
第一课内容比较详细的讲解了java程序的执行过程,但是感觉提出的问题并不能在文章中找到答案,也许是一个课后需要自己找寻答案的提问?还是希望可以有一个比较全面的回答的
解答
提出的问题是给大家的思考题,第二天会给出简单的解释,但是其实理解了文章的内容,完全可以自己找资料去理解,这是一个小作业,是一个思考的过程



问题七


问题
Object Header(4字节) + Class Pointer(4字节)+ Fields(看存放类型),但是jvm内存占用是8的倍数,所以结果要向上取整到8的倍数
解答
很好,就是这样


问题八



问题
如果我有一个静态的成员变量int,那我多线程更改是否会有线程安全问题,为什么?
解答
静态成员变量,他在内存里,只有一份,就是属于类的。你多个线程并发修改,一定会有并发问题,可能导致数据出错。



问题九



问题
类加载是按需加载,可以一次性加载全部的类吗?
解答
如果是默认的类加载机制,那么是你的代码运行过程中,遇到什么类加载什么类。如果你要自己加载类,那么需要写自己的类加载器


问题十



问题
为什么必须要一级一级类加载器的往上找,直接从顶层类加载器开始找不就行了吗?
解答

其实关于这个问题,不用过于纠结,每一层类加载器对某个类的加载,上推给父类加载器,到顶层类加载器,如果发现自己加载不到,再下推回子类加载器来加载,这样可以保证绝对不会重复加载某个类。

至于为什么不直接从顶层类加载器开始找,那是因为类加载器本身就是做的 父子关系模型

你想一下Java代码实现,他最底下的子类加载器,只能通过自己引用的父类加载器去找。如果直接找顶层类加载器,不合适的,那么顶层类加载器不就必须硬编码规定了吗?

这就是一个代码设计思想,保证代码的可扩展性。



问题十一



问题
是在执行new replicamanager()这行代码的时候加载replicamanger类吗?还是说加载cafka的时候就同时加载了呢?
解答
执行new ReplicaManager的时候加载类


问题十二



问题
还是没有明白 jvm和平时运行在机器上的系统之间是什么关系呢
解答
其实很简单,你运行在机器上的系统,其实就是一个JVM进程,JVM进程会执行你系统里写好的那些代码


问题十三



问题
  1. class文件分配内存是在准备阶段,那类的class对象是在准备阶段创建的吗?

  2. 如果实例变量有初始值,那实例变量是和类变量一同在初始化阶段赋值的吗?

  3. 初始化之后是不是就有实例了

解答
  1. 类是在准备阶段分配内存空间的

  2. 实例变量得在你创建类的实例对象时才会初始化

  3. 类的初始化阶段,仅仅是初始化类而已,跟对象无关,用new关键字才会构造一个对象出来



问题十四



问题
双亲委派可以解决类重复加载的问题。按照文章中介绍每个类加载器有不同的类加载路径,这些类加载路径是否可能重叠?
解答
不同类加载器的路径,一般是不会重叠的


问题十五



问题
自定义的类加载器本身是由系统加载器加载的,也就是说其本身是没有加密的,那么我拿到该类反编译就可以看到如果解密class文件了,请问老师是这样么?
解答
是的,所以说对class文件需要做 特殊混淆处理 ,有商用的产品可以用


问题十六



问题
作为一个web容器,既要解决跨应用公共共享问题也要解决独立应用独立问题。tomcat必须支持多层级的自定义类加载器
解答
很好的推测,明天会给出答案


问题十七



问题

用户使用类的时候应该是希望类已经准备好了一些数据,我猜想jvm设计者设计先执行static代码块的机制,是希望开发者在这里把使用类之前需要准备的工作在这里准备好

  1. 为什么类的初始化需要执行静态代码块,给静态成员变量赋值,是因为这些数据是在方法区吗?

  2. 启动类、扩展类和自定义加载器都已经指定了加载路径,所以不应该会有重复加载类的问题吧,所以双亲委派是不是没有必要

解答
  1. 没错,必须有初始化过程,准备好类级别的数据

  2. 双亲委派,避免重复加载,评论区里多次回复了这个问题,可以看一看


问题十八



问题

其实初始化时机就是对类的主动使用:调用静态方法时对类的主动使用的一种场景,main方法本质上是个static方法,没有调用的main方法和没有调用的static方法没区别!

有一个问题,包含main方法的类会优先加载,如果一个项目中有多个类中都有main方法,都会加载么?

解答
不会的,你启动一个jar包,需要指定某个main主类,优先就是加载他


问题十九



问题

tomcat本身是java程序,那么tomcat的实现程序的class是由应用类加载器加载的,用户自己的java程序war包,放入tomcat的程序的classpath中

这样用户的程序和tomcat的程序都是由应用类加载器加载了,也就是处于一个jvm中了

解答
非常好的回复,明天文章会给出答案


问题二十



问题
有一个问题,包含main方法的类会优先加载,如果一个项目中有多个类中都有main方法,都会加载么?
解答
你启动一个jar包的时候,会指定是走哪个main方法所在的类,是唯一的



问题二十一



问题
  1. 为什么类的初始化需要执行静态代码块,给静态成员变量赋值,是因为这些数据是在方法区吗?

  2. 启动类、扩展类和自定义加载器都已经指定了加载路径,所以不应该会有重复加载类的问题吧,所以双亲委派是不是没有必要

解答
  1. 没错,类在方法区,他在内存里,所以你必须给他初始化,赋值

  2. 还是有必要,比如启动类加载器,可以通过一些方式指定加载其他目录的类,那么你必须得走双亲委派,如果对那些特殊区域的类加载,走双亲委派,才能上推到启动类加载器去执行,不会重复加载



问题二十二



问题
老师好请问类加载双亲委派机制 为什么要先找父加载 而不是自己找? 这种设计的好处是?
解答

好处就在于,每个层级的类加载器各司其职,而且不会重复加载一个类。

比如你代码里用两个不同层级的类加载器,都去尝试加载了某个类,如果有双亲委派机制,那么都会先找父类加载器去加载,如果加载到了,那么以后就只会是他去加载这个类。

否则如果没有双亲委派机制,那么岂不是两个不同层级的类加载器可以加载同一个类,造成类的重复加载!



问题二十三



问题
自定义类加载器如何实现?
解答
自己写一个类,继承ClassLoader类,重写类加载的方法,然后在代码里面可以用自己的类加载器去针对某个路径下的类加载到内存里来


问题二十四



问题
看到一个词:动态部署,那么是否也有对应的静态部署?如何解释呢? (谢老师回答)
解答

假设一个背景在Tomcat部署系统的话,那么动态部署,也成为热部署

就是直接系统放入Tomcat对应目录,他自动就重新加载你最新的代码给你热部署了,不需要对Tomcat进行停机再重启;

反之,则是先停止Tomcat,然后部署最新代码到Tomcat对应目录里,然后重启Tomcat








请到「今天看啥」查看全文