30道“热乎乎”的 JVM 典型题目剖析!
- 2019 年 10 月 6 日
- 筆記
问题一
问题
方法走完,引用消失,堆内存还未必消失。好多人在做报表导出的时候,就会在for循环里不断的创建对象,很容易造成堆溢出,请问这种大文件导出怎么破?
解答
建议不要在for里创建对象,可以在外面搞一个对象,for循环里对一个对象修改数据即可
问题二
问题
1.Java支持多线程,每个线程有自己的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进程会执行你系统里写好的那些代码
问题十三
问题
- class文件分配内存是在准备阶段,那类的class对象是在准备阶段创建的吗?
- 如果实例变量有初始值,那实例变量是和类变量一同在初始化阶段赋值的吗?
- 初始化之后是不是就有实例了
解答
- 类是在准备阶段分配内存空间的
- 实例变量得在你创建类的实例对象时才会初始化
- 类的初始化阶段,仅仅是初始化类而已,跟对象无关,用new关键字才会构造一个对象出来
问题十四
问题
双亲委派可以解决类重复加载的问题。按照文章中介绍每个类加载器有不同的类加载路径,这些类加载路径是否可能重叠?
解答
不同类加载器的路径,一般是不会重叠的
问题十五
问题
自定义的类加载器本身是由系统加载器加载的,也就是说其本身是没有加密的,那么我拿到该类反编译就可以看到如果解密class文件了,请问老师是这样么?
解答
是的,所以说对class文件需要做特殊混淆处理,有商用的产品可以用
问题十六
问题
作为一个web容器,既要解决跨应用公共共享问题也要解决独立应用独立问题。tomcat必须支持多层级的自定义类加载器
解答
很好的推测,明天会给出答案
问题十七
问题
用户使用类的时候应该是希望类已经准备好了一些数据,我猜想jvm设计者设计先执行static代码块的机制,是希望开发者在这里把使用类之前需要准备的工作在这里准备好
- 为什么类的初始化需要执行静态代码块,给静态成员变量赋值,是因为这些数据是在方法区吗?
- 启动类、扩展类和自定义加载器都已经指定了加载路径,所以不应该会有重复加载类的问题吧,所以双亲委派是不是没有必要
解答
- 没错,必须有初始化过程,准备好类级别的数据
- 双亲委派,避免重复加载,评论区里多次回复了这个问题,可以看一看
问题十八
问题
其实初始化时机就是对类的主动使用:调用静态方法时对类的主动使用的一种场景,main方法本质上是个static方法,没有调用的main方法和没有调用的static方法没区别!
有一个问题,包含main方法的类会优先加载,如果一个项目中有多个类中都有main方法,都会加载么?
解答
不会的,你启动一个jar包,需要指定某个main主类,优先就是加载他
问题十九
问题
tomcat本身是java程序,那么tomcat的实现程序的class是由应用类加载器加载的,用户自己的java程序war包,放入tomcat的程序的classpath中
这样用户的程序和tomcat的程序都是由应用类加载器加载了,也就是处于一个jvm中了
解答
非常好的回复,明天文章会给出答案
问题二十
问题
有一个问题,包含main方法的类会优先加载,如果一个项目中有多个类中都有main方法,都会加载么?
解答
你启动一个jar包的时候,会指定是走哪个main方法所在的类,是唯一的
问题二十一
问题
- 为什么类的初始化需要执行静态代码块,给静态成员变量赋值,是因为这些数据是在方法区吗?
- 启动类、扩展类和自定义加载器都已经指定了加载路径,所以不应该会有重复加载类的问题吧,所以双亲委派是不是没有必要
解答
- 没错,类在方法区,他在内存里,所以你必须给他初始化,赋值
- 还是有必要,比如启动类加载器,可以通过一些方式指定加载其他目录的类,那么你必须得走双亲委派,如果对那些特殊区域的类加载,走双亲委派,才能上推到启动类加载器去执行,不会重复加载
问题二十二
问题
老师好请问类加载双亲委派机制 为什么要先找父加载 而不是自己找?这种设计的好处是?
解答
好处就在于,每个层级的类加载器各司其职,而且不会重复加载一个类。
比如你代码里用两个不同层级的类加载器,都去尝试加载了某个类,如果有双亲委派机制,那么都会先找父类加载器去加载,如果加载到了,那么以后就只会是他去加载这个类。
否则如果没有双亲委派机制,那么岂不是两个不同层级的类加载器可以加载同一个类,造成类的重复加载!
问题二十三
问题
自定义类加载器如何实现?
解答
自己写一个类,继承ClassLoader类,重写类加载的方法,然后在代码里面可以用自己的类加载器去针对某个路径下的类加载到内存里来
问题二十四
问题
看到一个词:动态部署,那么是否也有对应的静态部署?如何解释呢?(谢老师回答)
解答
假设一个背景在Tomcat部署系统的话,那么动态部署,也成为热部署
就是直接系统放入Tomcat对应目录,他自动就重新加载你最新的代码给你热部署了,不需要对Tomcat进行停机再重启;
反之,则是先停止Tomcat,然后部署最新代码到Tomcat对应目录里,然后重启Tomcat
问题二十五
问题
-XX:+TraceClassLoading 可以看加载了哪些类,动手实验了一下,jrelibrt.jar下的类全部加载了,其他都是用到时候加载。
解答
没错,明天更新的第三篇文章里,会讲解类加载机制,rt.jar这属于核心类库,属于支撑我们Java系统运行的底层类库,所以他一定会被加载
我们自己写的代码,一般是你代码运行使用到了哪个类,就会去加载哪个类
问题二十六
问题
老师,类加载器是把jar包里的所有类一次性全部加载进去吗?
解答
不是的,首先加载包含main方法的主类,接着是运行你写的代码的时候,遇到你用了什么类,再加载什么类
二十七
问题
通过代码混淆机制,加大反编译之后的可读性!或者是否可以基于二进制加密呢,学生没用过!
解答
其实现在对于这个一般都是用商业产品的,有很多第三方公司提供加密产品,可以百度一下,class文件加密,就可以看到,直接用他们的产品即可
问题二十八
问题
Class源文件的保护,可以采用代码混淆技术,方式有很多,如回答区中老师提到的商用加密软件
解答
非常好,就是这样
问题二十九
问题
看文中内容,是会加载两次字节码吗,第一次加载进jvm,然后程序执行的时候再加载。有点不解!
解答
你好,不是加载两次,是JVM先把“.class”字节码文件中的类加载到内存里,然后执行的时候,就直接使用加载好的类即可,不会重复加载
问题三十
问题
class文件通过工具可以反编译的,请问有没有方法对class文件进行加密又不影响它的执行。windows桌面程序里一般都是打包成dll文件,java中有没有比较好的方式?
解答
可以的,比如jvmti小工具就可以实现class文件的加密
另外其实为了保护源代码安全,有很多商业公司推出了专业级别的class加密产品,可以付费使用。
解密的话一般可以基于自定义的类加载器来实现,在加载类的时候把class给解密,这样就可以保护自己的源代码安全了。
最后,附上两张Tomcat类加载如果按委派模型的加载流程和实际实现的流程(专栏读者所画)