深入理解java虚拟机

2019-04-07  本文已影响0人  清雨季

这个文章是自己看书记录用于以后复习用的,不适用于大家学习,如果不小心搜索到,那现在可以直接关闭了

面试题收集:

1、你知道哪些或者你们线上使⽤什么GC策略?它有什么优势,适⽤于什么场景?
2、Java类加载器包括⼏种?它们之间的⽗⼦关系是怎么样的?双亲委派机制是什么意思?有什么好处?
3、如何⾃定义⼀个类加载器?你使⽤过哪些或者你在什么场景下需要⼀个⾃定义的类加载器吗?
4、堆内存设置的参数是什么?
5、Perm Space中保存什么数据?会引起OutOfMemory吗?
6、做GC时,⼀个对象在内存各个Space中被移动的顺序是什么?
7、你有没有遇到过OutOfMemory问题?你是怎么来处理这个问题的?处理 过程中有哪些收获?
8、JDK 1.8之后Perm Space有哪些变动? MetaSpace⼤⼩默认是⽆限的么? 还是你们会通过什么⽅式来指定⼤⼩?
9、jstack 是⼲什么的? jstat 呢?如果线上程序周期性地出现卡顿,你怀疑可 能是 GC 导致的,你会怎么来排查这个问题?线程⽇志⼀般你会看其中的什么 部分?
10、StackOverflow异常有没有遇到过?⼀般你猜测会在什么情况下被触发?如何指定⼀个线程的堆栈⼤⼩?⼀般你们写多少?

一 运行时数据区域

1.1.1 程序计数器

线程私有,用于记录当前线程运行到的位置,以便随时恢复线程

1.1.2 Java虚拟机栈

线程私有,包括栈帧,局部变量表,操作数栈,动态链接,方法出口
局部变量表保存8种基本类型的局部变量,以及引用类型的指针
栈帧对应着一个方法

1.1.3 本地方法栈

与Java虚拟机栈类似,但是是用于支持native方法的执行的,在hotspot虚拟机中,Java虚拟机栈与本地方法栈共用

1.1.4 Java 堆

所有线程共享,垃圾收集器的主要区域,可以分为新生代和老年代,新生代又分为Eden space, From survivor space , To survivor space。
从内存分配的角度看,可以分为多个线程私有的的分配缓冲区
通过-Xmx -Xms 控制大小

1.1.5 方法区

存放类信息,常量,静态变量,即时编译器编译后的代码等数据
--XX:MaxPermSize 指定上限
这一区域的垃圾回收目标主要是针对常量池的回收和类型的卸载

1.1.6 运行时常量池

这是方法区的一部分,用于存放编译期生成的各种字面量和符号引用

1.1.7 对象的创建

1.1.8 对象的内存布局

对象在内存中可以分为:对象头(Header)实例数据(Instance Data)和对齐填充(Padding)
对象头(储存对象自身的运行时数据):哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳,类型指针(有的虚拟机可能没有),如果是数据对象,还要记录长度
实例数据:默认分配策略会把同大小的分配到一起,父类的变量会出现在子类之前,但是子类中较窄的变量也可能会插入到父类的间隙中去。
对齐填充:HotSpot VM 要求对象的起始地址必须是8字节的整数倍,所有要填充

1.1.9 对象的访问定位

句柄访问:java堆中划分一块内存来作为句柄池,reference保存的是句柄池的地址,句柄池中包含对象的实例数据指针和对象的类型指针。缺点是多一次访问,优点是reference不用变。
直接指针访问:reference直接保存对象实例数据地址,对象头中包含元类型数据地址
HotSpot使用的是直接访问的方式

二 垃圾收集器与内存分配策略

2.1 对象已经死了吗

引用计数法
可达性分析算法:GC Roots 包括虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象

2.2 引用类型

强引用:普通new Object()产生的引用
软引用:内存溢出时会回收
弱引用:下次GC时一定会回收
虚引用:无法通过虚引用来找到一个对象,这种引用只是想在对象回收时收到一个通知

2.3 finalize()方法

对象第一次可达性分析,如果没有与GC ROOTs相连,则会调用finalize方法,在finalize方法中可以自救,但是finalize方法只会被调用一次

2.4 回收方法区

方法区回收的效果不好,主要是回收废弃常量和无用的类
废弃常量:与堆中的对象类似,没有任何引用的对象
无用的类:必须要满足以下三个条件,才会被回收

参数-Xnoclassgc 参数可以控制是否回收无用的类

2.5 垃圾回收算法

标记清除法(Mark-Sweep):算法分为标记-清除两个阶段

复制算法:每次只用一块,用完时把对象复制到另一块中

标记整理算法(Mark-Compact): 先标记,然后把存活对象往一边移动

分代回收的思想:新生代使用复制算法,老年代使用标记-清理或者标记-整理算法

2.6 HotSpot 算法实现

2.6.1.Serial收集器

虚拟机在Client模式下新生代的默认收集器,单线程,采用复制算法

2.6.2 ParNew收集器

与Serial收集器一样,只是回收新生代时采用的是多线程
这是Server模式下新生代的首选收集器
-XX:SurvivorRatio:Eden区:两个Survivor区 的比值
-XX:ParallelGCThreads : 指定线程的数量
-XX:+UseParNewGC强制指定使用这种收集器

2.6.3 Parallel Scavenge 收集器

新生代收集器,与ParNew一样使用的是复制算法,多线程,但是这个收集器的目标是使垃圾收集的时间尽量短
-XX MaxGCPauseMillis:控制最大垃圾收集时间
-XX:GCTimeRatio:用来控制吞吐量
-XX:UseAdptiveSizePolicy:自动调用新生代大小,Eden和Survivor区的比例

2.6.4 Serial Old 收集器

Serial 收集器的老年代版本,单线程,使用标记整理算法

2.6.5 Parallel Old 收集器

Parallel Scavenge 收集器的老年代版本,多线程,标记-整理算法

2.6.5 CMS收集器

以最短停顿时间为目标,标记清除算法
步骤一:初始标记:从GC Roots 递归找到所有存活对象
步骤二:并发标记:GC Roots Tracing过程
步骤三:重新标记:修正并发标记期间的变动
步骤四:并发清除:清除对象
只有初始标记和重新标记会暂停用户线程
缺点:

2.6.6 对象分配策略

三 常用监控分析工具

3.1 jps
上一篇 下一篇

猜你喜欢

热点阅读