jvm的知识体系
jvm分代算法
堆内存的逻辑分区
当创建一个对象首先会在eden区分配空间,如果分不开,直接进入老年代。
在eden区进行YGC,这时大多数的对象会被回收干掉,存活的对象放入s0,再次YGC
,包括eden区和s0,存活的对象会进入到s1区。之后再YGC,eden+s1->s0,回收一次年龄增加一岁,年龄足够大进入老年代。
垃圾回收器
STW(stop the world)停下用户线程,当触发垃圾回收机制,停下用户线程,然后单线程垃圾回收。回收后,程序继续运行,再次触发GC,停下来用户线程,垃圾回收再上场。
ParNew和cms配合并行回收
6.ConcurrentMarkSweep:老年代 垃圾回收器在运行,应用程序同时也在运行,降低STW的时间(200ms以内)
7.G1
8.ZGC(1ms) PK C++
9.Shenandoah
10.Eplison
JDK1.8默认的垃圾回收器:PS + ParallelOld
如果可以分配在栈空间,当然最好,因为方法在栈空间是以栈帧的形式存在的,执行效率高。但是前提是该对象在占空间不能存在逃逸情况。如果对象的内存很大直接会分配在老年代。如果对象内存小,可以分配在ThreadLocalAllocateBuffer本地线程缓冲区,之后进入eden区,经过YGC进入S1,此后S1和S2循环配合,够年龄后,直接进入老年代。
CMS 老年代 垃圾回收器在运行,应用程序同时也在运行,降低STW的时间(200ms以内)
初始标记:CMS开始介入,STW,把根对象先标记,因此停顿时间非常短。
并发标记:由初始标记后的根对象,进行下一步的跟踪搜索垃圾。
重新标记:在标记的过程中,有些对象被标记为垃圾,但是又有新的引用的,这是需要进行重新标记,把错误重新揪出来。
并发清理:进行处理
先找出根对象
根对象有:线程栈变量,静态变量,常量池,JNI指针,本地方法栈,Clazz
由根对象往后找
有个对象本来是垃圾,但是又重新获得引用。remark
最后,把垃圾全部清除。
常见jvm问题:JVM-解释一下对象创建过程(半初始化)???
public class Hello {
volatile int i;
public void m(){
System.out.println("hello jvm");
}
public static void main(String[] args) {
Object o = new Object();
}
}
idea安装插件jclasslib,之后重启
首先new #5 先申请空间#5大小的空间
invokespecial #1 调用构造方法之后,才会有初始值。
astore_1建立关联
DCL(双重检查锁)与volatile问题??(指令重排)
volatile的作用:
①线程可见
public class ThreadVisibility {
private static volatile boolean flag=true;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while (flag){
}
System.out.println("end");
},"server").start();
Thread.sleep(1000);
flag=false;
}
}
一个server线程,一个主线程,本来server线程一直处于 循环状态,但是main线程把flag改为false,flag对server线程是可见的,所以结束循环打印end。
②禁止指令重排
DCL双重检查锁,经典用法是单例模式。
public class Mgr06{
问题是这里要不要加volatile
private static volatile Mgr06 INSTANCE;
private Mgr06(){}
public static Mgr06 getInstance(){
if(INSTANCE==null){//第一层主要是提高执行效率
synchronized(Mgr06.class){
if(INSTANCE==null){
try{
Thread.sleep(1);
}catch(Exception e){
e.printStackTrace();
}
INSTANCE=new Mgr06();
}
}
}
return INSTANCE;
}
}
3.对象在内存中的存储布局
Class T{
int m=8;
}
普通对象:markword 8字节(64位虚拟机)
类型指针Class pointer T t=new T();相当于有个类指针T.class指向对象t,知道这个对象属于哪个类的对象。
实例数据 相当于m
padding对齐,主要是为了提高效率。
import org.openjdk.jol.info.ClassLayout;
class T{
int m;
}
public class Hello {
public static void main(String[] args) {
T t = new T();
System.out.println(ClassLayout.parseInstance(t).toPrintable());
}
}
markword 4+4
ClassPointer 4
instance data int类型占 4字节
一共16字节
使用带压缩
markword 4+4字节
ClassPointer 4+4字节
instance data int类型占 4字节
padding 4字节
使用UseCompressed时,ClassPointer为4字节
对象头具体包括什么???
换言之为markword头包括什么。包括三部分,hashcode,GC信息,对象锁信息。
import org.openjdk.jol.info.ClassLayout;
public class Hello {
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o){
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}
为什么GC年龄默认为15?
(最大为15)垃圾回收器PS+PO 的年龄为15(markword占四位二进制,最大为15)
CMS的年龄为6
对象怎么定位??
句柄的优点:对象小,垃圾回收时,不用频繁改动
缺点:两次访问
直接指针的优点:直接访问
缺点:GC需要移动对象的时候稍麻烦
对象怎么分配的???
创建一个对象首先看是否适合在栈中分配,如果适合在栈中分配,随着栈帧的pop,对象也会被回收。如果不能在栈中分配,那么要看对象的内存多大,如果内存过大,会直接进入老年代,之后经过FGC被回收。
如果对象内存不大,会尝试在TLAB(线程本地分配缓冲区)分配,不管分配成功是否,都会到伊甸区eden区中,其实在eden区为每个线程划分了很多小的自己的私有区域,这样效率比较高。当eden装不下时,小GC到S1,年龄够,到老年区,年龄不够,复制到S2,S1和S2配合循环往复。
7.Object o =new Object()在内存占多少字节?
16个字节