一、GC调优
- 确定调优目标,选择合适的回收期:
(1)低延迟:CMS、G1、ZGC;
(2)吞吐量:ParallelGC。 - 调优之前先确定代码问题:数据是否太多、数据表示是否太臃肿、是否存在内存泄露。
- 从新生代调优开始:内存空间越大越好。
(1)新生代的大小建议设置为堆内存大小的25%~50%之间;
(2)新生代大小的理想内存是能容纳 【并发量*(请求 - 相应)】的数据;
(3)幸存区的内存设置要大到能保留(当前活跃对象+需要晋升对象);
(4)把存活时间长的对象尽快晋级(-XX:MaxTenuringThreshold=threshold,设置晋升阈值) - 老年代调优:内存空间越大越好。
(1)发生 Full GC 时,可将老年代的内存预设调大 1/3~1/4;
(2)-XX:CMSInitiatingOccupancyFraction=percent,设置老年代占用多长时间时调用CMS; - 一些VM参数:
(1)-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld,启动垃圾回收器;
(2)-XX:+CMSScavengeBeforeRemark,在 CMS 的 Remark 之前对新生代进行垃圾回收。
二、Javap工具
反编译 class 文件,格式:在控制台中(javap -v 类名.class)。
- 代码运行时
(1)class 文件中的常量池载入运行时常量池中;
(2)class 文件中方法的执行指令存入到方法区中;
(3)栈帧中有局部变量表(存放局部变量)和操作数栈(存放操作数)。 - 扩展
(1)cinit()方法:类加载时从上到下收集类中的静态变量和静态代码块中的代码按顺序执行。
(2)init()方法:对象创建时从上到下收集初始化代码块“{}”和成员变量的赋值语句,然后把原构造方法中的语句加到最后构成新的构造方法。
(3)try-catch-finally:假设 try 中含有 return 语句,- 若 finally 中也存在 return 语句,因为 finally 中的代码紧跟在 try 代码的后面,所以 finally 代码会执行。但 finally 中存在 return 的话会把未被 catch 的异常给吃掉,不会抛出;返回时返回 finally 中的 return。
- 若 finally 中不存在 return ,finally 中的代码还是会执行,但返回的是离 return 最近的修改,即使 finally 中又做了修改也没用。
- 结论:return 会返回离 return 最近的修改。
三、运行期优化
- 逃逸分析:对于大部分不常用代码,直接采取解释执行的方式运行;对于小部分的热点代码,可以将其编译成机器码,以达到理想的运行速度。
- 解释器与即时编译器(JIT)的区别:
(1)解释器将字节码解释为机器码,即使下次遇到相同字节码,仍会重复解释;
(2)JIT 是将字节码编译成机器码存入 Code Cache,下次遇到相同字节码是会直接执行,无序再编译;
(3)解释器将字节码解释成平台通用的机器码;
(4)JIT 会根据平台类型生成特定代码。 - 方法内联:被调用方法的代码块不大长时,会复制粘贴代码到方法调用的位置;
- 反射优化:当使用反射调用某个类的方法次数达到一个阈值后,会直接通过该类调用方法。