跳到主要内容

05、JVM 调优实战 - 知识点归纳

1、 方法走完,引用消失,堆内存未必消失个别在做报表导出的逻辑中,会在for循环里不断的创建对象,很容易造成对溢出;

面对上述的问题,建议不要在for 里创建对象,可以在外面搞一个对象, for 循环里对一个对象修改数据即可。

2、 Java支持多线程,每个线程都有自己的Java虚拟机栈和本地方法栈新建的实例在堆内存,实例变量也在堆内存;

3、 所谓出栈、入栈,入栈,就是你执行一个方法的时候,为这个方法创建一个栈帧入栈;出栈,就是方法执行完毕了,就会出栈;

4、 父类子类的情况下,加载顺序如下:;

加载父类就是父类,除非用到子类才会加载子类;但是加载子类要初始化之前,必须先加载父类,初始化父类。

5、 类的实例化,是先加载类,再实例化对象类加载器有三层,如果在第二层的类加载器可以加载这些类的话,就没有必要往上去找他的父类加载;其次,类只有用到的时候才加载到内存中,而new对象的时候肯定用到,需要先经历过类的所有过程才将类实例化;

6、 ObjectHeader(4字节)+ClassPointer(4字节)+Fields(看存放类型),因为JVM内存占用是8的倍数,所以结果要向上取整到8的倍数;

7、 静态成员变量,它在内存里,只有一份,就是属于类的如果有多个线程并发修改,一定会有并发问题,可能导致数据出错;

8、 在类加载机制中如果是默认的类加载机制,那么是你的代码运行过程中,遇到什么类加载什么类如果你要自己加载类,就需要写自己的类加载器;

9、 为什么必须要一级一级类加载器的网上招,直接从顶层类加载器开始找不就行了吗?;

答:每一层类加载器对某个类的加载,上推给父类加载器,到顶层类加载器,如果发现自己加载不到,再下推回子类加载器来加载,这样就可以保证绝对不会重复加载某个类。

而不直接从定层类加载请开始找,是因为类加载器本身就是做的父子关系模型。这种模型场景下,最底下的子类加载器,只能通过自己引用的父类加载器去找。如果直接找到顶层类加载器,不合适,那么顶层类加载器就必须硬编码规定了。

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

10、 在执行newReplicaManager的时候加载ReplicaManager类;

11、 我们运行在机器上的系统,其实就是一个JVM进程,JVM进程会执行你系统里写好的那些代码;

12、

1、 class文件分配内存是在准备阶段,而类的class对象也是在准备阶段分配内存空间;

2、 如果实例变量有初始值,那么实例变量得在创建类的实例对象时才会初始化;

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

13、 双亲委派可以解决类重复加载的问题,虽然每个类加载器有不同的类加载路径,但不同类加载器的路径,一般是不会重叠的;

14、 自定义的类加载器本身是由系统加载器加载的,也就是说其本身是没有加密的,那么我拿到该类反编译就可以看到解密的class文件了;

因此,对 class 文件需要做特殊混淆处理,有商用的产品可以用。

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

包含main方法的类会优先加载,如果一个项目中有多个类都有 main 方法,并不会都加载,因为启动一个 jar 包,需要指定某个 main 主类,优先就是加载它。

16、 类的初始化需要执行静态代码块,给静态成员变量赋值,是因为这些数据都在方法区类在方法区里,它在内存里,所以必须给他初始化,赋值;

启动类、扩展类和自定义加载器都已经指定了加载路径,所以不应该会有重复加载类的问题。但是双亲委派还是必要的,比如启动类加载器,可以通过一些方式指定加载其他目录的类,那额必须得走双亲委派,如果对那些特殊区域的类加载,走双亲委派,才能上推到启动类加载器去执行,不会重复加载。