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

366 篇文章 2 订阅
232 篇文章 5 订阅

写在前面

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

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

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

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

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

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

问题一

问题

方法走完,引用消失,堆内存还未必消失。好多人在做报表导出的时候,就会在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进程会执行你系统里写好的那些代码

 

问题十三

问题

  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

 

问题二十五

问题

-XX:+TraceClassLoading 可以看加载了哪些类,动手实验了一下,jre\lib\rt.jar下的类全部加载了,其他都是用到时候加载。

解答

没错,明天更新的第三篇文章里,会讲解类加载机制,rt.jar这属于核心类库,属于支撑我们Java系统运行的底层类库,所以他一定会被加载

我们自己写的代码,一般是你代码运行使用到了哪个类,就会去加载哪个类

 

问题二十六

问题

老师,类加载器是把jar包里的所有类一次性全部加载进去吗?

解答

不是的,首先加载包含main方法的主类,接着是运行你写的代码的时候,遇到你用了什么类,再加载什么类

 

问题二十七

问题

通过代码混淆机制,加大反编译之后的可读性!或者是否可以基于二进制加密呢,学生没用过!

解答

其实现在对于这个一般都是用商业产品的,有很多第三方公司提供加密产品,可以百度一下,class文件加密,就可以看到,直接用他们的产品即可

 

问题二十八

问题

Class源文件的保护,可以采用代码混淆技术,方式有很多,如回答区中老师提到的商用加密软件

解答

非常好,就是这样

问题二十九

问题

看文中内容,是会加载两次字节码吗,第一次加载进jvm,然后程序执行的时候再加载。有点不解!

解答

你好,不是加载两次,是JVM先把“.class”字节码文件中的类加载到内存里,然后执行的时候,就直接使用加载好的类极客,不会重复加载

 

问题三十

问题

class文件通过工具可以反编译的,请问有没有方法对class文件进行加密又不影响它的执行。windows桌面程序里一般都是打包成dll文件,java中有没有比较好的方式?

解答

可以的,比如jvmti小工具就可以实现class文件的加密

另外其实为了保护源代码安全,有很多商业公司推出了专业级别的class加密产品,可以付费使用。

解密的话一般可以基于自定义的类加载器来实现,在加载类的时候把class给解密,这样就可以保护自己的源代码安全了。

最后,附上两张Tomcat类加载如果按委派模型的加载流程和实际实现的流程(专栏读者所画)

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

 

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

 

这些,就是本周展示的典型问题了。你都理解了吗?

个人公众号:Java架构师联盟,每日更新技术好文

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值