身边有同学实习面试被问,JVM性能调优等问题,来总结一下JVM
如对象组成中的分代年龄(4bit),跟GC的分代相关;
什么是程序计数器,作用?
什么是 Java 虚拟机栈,作用?
什么是 本地方法栈,作用?
什么是 堆,作用?
什么是方法区,作用?
说出java文件运行的整个过程?
java类文件转换为class文件,在经过类装载系统,到达运行时数据区运行(我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间));运行时数据区包括(程序计数器,虚拟机栈,本地方法栈,堆(线程共享),方法区(线程共享));
Java虚拟机栈存放着许多栈帧,每个方法执行的同时都会创建一个栈帧(stack Frame),方法执行完成,出栈;整个过程可以看成在虚拟机栈的入栈出栈过程;
帧结构:1. 局部变量表,2. 操作数栈,3.动态链接,4.方法出口
发现java文件与对应字节码文件的关系
public int compute() { #java文件
int a = 0;
int b = 1;
int c = (a + b) * 10;
return c;
}
public int compute(); #对应的字节码
Code:
0: iconst_0 #
1: istore_1
2: iconst_1
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: bipush 10
9: imul
10: istore_3
11: iload_3
12: ireturn
GC是什么,工作方式和区域等?
public class GCtest {
//100kb
byte[] a = new byte[1024 * 100];
public static void main(String[] args) throws InterruptedException {
ArrayList<GCtest> gCtests = new ArrayList<>();
while (true) {
gCtests.add(new GCtest());
Thread.sleep(10);
}
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.gjf.GCtest.<init>(GCtest.java:13)
at com.gjf.GCtest.main(GCtest.java:17)
Arthas(阿尔萨斯)是阿里巴巴开源的 Java 诊断工具,深受开发者喜爱。
减少GC,特别是FullGC
- 对象优先在堆的 Eden 区分配
- 大对象直接进入老年代
- 长期存活的对象将直接进入老年代. 当 Eden 区没有足够的空间进行分配时,虚拟机会执行一次 Minor GC.Minor Gc 通 常发生在新生代的 Eden 区,在这个区的对象生存期短,往往发生 Gc 的频率较高, 回收速度比较快;Full Gc/Major GC 发生在老年代,一般情况下,触发老年代 GC 的时候不会触发 Minor GC,但是通过配置,可以在 Full GC 之前进行一次 Minor GC 这样可以加快老年代的回收速度。
- GC 的两种判定方法:
- 引用计数法:指的是如果某个地方引用了这个对象就+1,如果失效了就-1,当为 0 就 会回收但是 JVM 没有用这种方式,因为无法判定相互循环引用(A 引用 B,B 引用 A) 的情况
- 引用链法: 通过一种 GC ROOT 的对象(方法区中静态变量引用的对象等-static 变 量)来判断,如果有一条链能够到达 GC ROOT 就说明,不能到达 GC ROOT 就说明 可以回收
是指使用单线程进行垃圾回收的回收器,每次回收只有一个工作线程(对并行能力比较弱的电脑,运行性能较好)
注意:串行回收器运行时,所有
应用程序的线程都停止工作,属于独占式的垃圾回收方式
;线程进行等待的现象称为–>Stop-The-World,造成非常糟糕的用户体验;
多个线程同时进行垃圾回收,适合并行能力强的计算机;
- 新生代ParNew回收器,只是简单将串行回收器多线程化,也是独占式的回收器
- 还有其他的回收器,都
关注吞吐量
,其中包括复制算法,标记压缩算法等回收算法注意:还是会造成线程等待现象–>Stop-The-World(STW),但是减少垃圾回收的停顿时间就会同时减小系统的吞吐量
CMS回收器主要关注
系统的停顿时间
,并发标记清除,是一个基于标记清除算法的回收器
;CMS掉工作过程相对复杂,不是独占式的回收器,工作过程中,应用程序仍然工作;
不会等到堆内存饱和后进行回收,而是到达一定阈值才开始垃圾回收
参数 说明 -XX:CMSInitiatingOccupancyFraction 默认堆老年代使用达到68%,执行CMS回收,如果在执行过程中内存不足,就会启动串行回收器进行垃圾回收,应用程序将完全中断; 根据此参数进行调优,增大阈值可以降低CMS的触发,减少老年代的回收次数;如果内存使用增长很快,应该降低阈值; CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的垃圾收集器。从名字可以看出,CMS 是基于标记-清除算法的。它的运作过程主要分为四个步骤:
Garbages First(G1)垃圾回收器,作为CMS的长久替代方案,使用了全新的分区算法;
- 并行性:G1回收期间,多个GC线程可以同时工作
- 并发性:G1可以跟应用程序交替执行的能力,不会在回收期间完全阻塞应用程序
- 分代GC:G1依然是一个分代回收器,与之前回收器不同,G1兼顾年轻代和老年代,如CMS工作在老年代
- 空间整理:G1回收过程中,会适当进行对象移动,如CMS若干次GC后,CMS必须进行一次碎片整理,但是G1,每次回收都会有效复制对象,减少碎片空间;
- 可预见性:由于分区原因,G1只对选取的部分区域进行回收。可以很好的控制全局停顿;
eden区被占满,新生代GC启动,ednn区会被全部回收
,至少存在一个survivor区,老年代区域增大;
在并发标记后就知道哪个区域的垃圾较多,G1就会优先回收垃圾比例高的区域
堆内存不足时,就会触发FullGC,
对于并行回收器的FullGC之前,都会触发一次新生代GC
使用system.gc()方法,触发一次GC,在并行回收器中,FullGC之前会发生一个新生代GC,这样可以缩短停顿时间(STW)
-Xmx32m -Xms32m -XX:+UseSerialGC -XX:+PrintGCDetails
G1具备如下特点:
- 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短Stop-the-world停顿的时间,部分其他收集器原来需要停顿Java线程执行的GC操作,G1收集器仍然可以通过并发的方式让Java程序继续运行。
- 分代收集
- 空间整合:与CMS的标记-清除算法不同,G1从整体来看是基于标记-整理算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的。但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
- 可预测的停顿:这是G1相对于CMS的一个优势,降低停顿时间是G1和CMS共同的关注点。
CMS 用于老年代的回收,而 G1 用于新生代和老年代的回收。
G1 使用了 Region 方式对堆内存进行了划分,且基于标记整理算法实现,整体减少了垃圾碎片的产生。CMS使用“标记-清理”算法会产生大量的空间碎片;
-Xmx512m -XX:MaxPermSize=32M -Xloggc:gc:gc.log -XX:+PrintGCDetails\
//MaxPermSize为最大的永久区大小。
JVM中的内存区域一般分为3个部分: 年轻代、年老代和永久代;永久代在JDK 7中逐渐变化,到JDK 8之后完全消失,合并到了Native堆中,JDK8中,PermSize和MaxPermSize参数也一并移除了。
内存溢出(OOM)
待续,如何看问题做性能优化
程序计数器
内存空间小,线程私有。字节码解释器工作是就是通过改变这个计数器的值来选取下一条需要执行指令的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖计数器完成
如果线程正在执行一个 Java 方法,这个计数器记录的是
正在执行的虚拟机字节码指令的地址
;如果正在执行的是 Native 方法,这个计数器的值则为 (Undefined)。此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域
。
线程私有
,生命周期和线程一致。描述的是 Java 方法执行的内存模型:每个方法在执行时都会床创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束,就对应着一个栈帧从虚拟机栈中入栈到出栈的过程。**局部变量表:**存放了编译期可知的各种基本类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型)和 returnAddress 类型(指向了一条字节码指令的地址),第一位放的是0,代表this当前对象;
StackOverflowError:线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError:如果虚拟机栈可以动态扩展,而扩展时无法申请到足够的内存。本地方法栈
区别于 Java 虚拟机栈的是,Java 虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则使用 Native 方法。也会有 StackOverflowError 和 OutOfMemoryError 异常。
Java 堆
对于绝大多数应用来说,这块区域是 JVM 所管理的内存中最大的一块。线程共享,
主要是存放对象实例和数组。内部会划分出多个线程私有的分配缓冲区
(Thread Local Allocation Buffer, TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。(GC,G1主要工作区域)OutOfMemoryError:如果堆中没有内存完成实例分配,并且堆也无法再扩展时,抛出该异常。
方法区
属于
共享内存区域
,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
。
一键三连