Kosmos Kosmos

---我们总得选择一条路去前行---

目录
学习笔记|理解Java虚拟机
/      

学习笔记|理解Java虚拟机

类加载过程

java将源代码通过静态编译器转化成字节码文件,class字节码文件可以保证通过不同平台的解释器对class字节码文件来实现多处运行
字节码文件被classloader加载到java虚拟机的内存中构建为class对象(注意classload只是加载class文件到内存,还有没有进行链接和初始化),而class.forname会全做,所以classloader比forname运行快

加载:将class字节数据流加载到java内存中.创建Class实例
链接:验证class文件的正确性,给静态变量空间并设置内存值
初始化:赋值

字节码一般通过JIT混合执行模式加载到内存

解释器一开始使用解释执行,省去编译时间,然后Jvm使用JIT的动态编译技术将热点代码转化成机器码交给cpu执行

反射

在运行的时候可以得到任何类的信息(方法,变量之类的)
new可以调用任何构造方法,Class的newInstance只能调用无参构造方法
获取class:

class.forname(真实类路径)
xx.class
实例.class

Java引用

简记:强软弱虚

强引用:只要存在就不会被回收
软引用:会在内存不足的时候进行回收
弱引用:不管内存怎么样都会被回收
虚引用:不会影响生存时间
引用队列(ReferenceQueue):存储关联的且被GC回收的软引用,弱引用,虚引用(对象被回收,它们的引用会被存放到引用队列中)

对象的finalize()

尽量避免使用该方法拯救对象
当对象不在gc root的引用链中,虚拟机会标记对象并进行筛选:查看对象是否覆盖finalize()或者说finalize方法是否已经执行过了,假如对象没有覆盖或者已执行过,则直接被GC回收,否则就将对象放到F-queue队列中等待处理,为了防止出现死循环或者缓慢执行,虚拟机不承诺等待对象方法运行结束 -.- 对象可以将自身(this)赋值给某个变量防止被回收

classloader层级

自定义classloader->application classloader->extension classloader->bootstrap classloader
自定义classloader要继承classloader,重写findclass()方法,调用defineclass()方法
bootstrap classloader不存在JVM的体系中,所以extclassloader的getparents是null

双亲委派机制:从左往右检查类是否加载过,如果到了bootstrap classloader还没有加载过就从右往左尝试可否加载该类
自定义类加载器的好处:实现了不同中间件的类隔离,避免类的冲突,使每个类都能使用自己依赖的jar包,还能从不同地方进行加载(findclass通过不同方法得到字节流然后调用defineclass)

内存布局

线程私有:程序计数器(方法走到哪里的标识,辅助线程的执行和恢复),虚拟机栈(存放栈帧),本地方法栈
共享:java堆(垃圾回收的主要区域,放的是对象实例,常量池) 元数据(放类信息)

虚拟机栈

栈帧是方法运行的基本结构
栈帧里面有局部变量表(存放方法参数和局部变量),操作栈(方法过程中运算和存放数据的地方),动态链接,方法返回地址

java堆

分为新生代(eden和两个survivor)和老年代(old)

-Xss:每一个线程中虚拟机栈的大小
-Xms:堆的初始大小
-Xmx:堆的最大空间

minor gc=young gc 针对新生代
full gc针对整个堆
新对象在新生代,太大的放老年代,大部分对象在eden区,当eden满了调用younggc,有引用的对象被转到其中一个survivor,下一次ygc的时候将存活的对象放到另一块survivor区域,并清空当前空间,如果塞不下就放到老年代,新生代转移15次后转到老年代,老年代都放不下了就调用full gc

new出来的字符串实例存放在堆中,常量池会存放他的引用,""字符串直接放在常量池中

对象实例化过程

确认类元信息时候存在,不存在就进行加载,生成Class对象
分配对象内存
设置默认值(不同状态的零值)
设置对象头(对象的基本信息,如hash码,gc信息)
执行init方法,初始化成员变量,执行实例化代码块,调用构造方法,将堆中的对象地址赋值给引用变量

垃圾回收

废弃常量判断

没有任何对象引用常量

无用类判断

该类的所有实例都被回收
加载该类的classloader被回收
该类的Class对象没有被引用

垃圾的确定方法

1.引用计数法 缺点 无法判断循环引用
2.可达性分析法 通过gc root判断时候被引用
可作为gc root对象:
1.类静态属性中引用的对象
2.常量引用的对象
3.虚拟机栈中引用的对象
4.本地方法栈中引用的对象

垃圾回收算法

1.标记清除:会产生大量不连续的碎片,效率不高
2.标记整理:效率不高,但不会产生碎片
3.复制:运行高效,但内存减小一半,由老年代做后备仓库 -.-
4.分代回收(新生代使用复制,老年代使用标记清除或标记整理)

新生代变更快,使用复制;老年代存活率高,使用标记清除或标记整理 -.-

垃圾回收器

新生代用

Serial回收器

单线程(一个cpu或者一个收集线程并且工作时暂停其他所有工作线程) client

ParNew回收器

多线程的Serial回收器,也会在工作时暂停其他所有工作线程 server
######Parallel Scavenge回收器
注重吞吐量,尽快完成任务,而其他CMS等回收器注重缩短用户线程停顿的时间(用户交互) 自适应调节参数UseAdaptiveSizePolicy

老年代用

Serial Old回收器

单线程,工作原理和Serial一样

Parallel Old收集器

与Parallel Scavenge配合工作,工作原理和Parallel Scavenge一样

CMS回收器

4个步骤完成回收 1,3需要stop the world
1.初始标记:标记GC roots 能直接关联的对象
2.并发标记:标记堆中存活的对象,和用户线程一起工作
3.重新标记:修正并发标记期间用户线程导致标记产生变动的对象的标记记录
4.并发清除:清除未被标记的对象
隐藏步骤5:重置线程
缺点:
1.对cpu资源敏感,cpu资源不足时会影响用户程序运行速度
2.并发清理阶段产生的垃圾要等到下一次GC时才能回收
3.会产生大量碎片,没有连续空间存放对象时会出发full gc

不刻意区分新生代和老年代

G1回收器

将java堆划分为大小相同的区域(region)
region一共有4种:eden,survivor,old,humongous(特殊的old,专门放大型对象)
一个region中的对象可以被全堆中的对象引用
使用remembered set避免进行全局扫描,当虚拟机发现程序在对reference类型数据写操作时,判断引用的对象是否在同一region,如果是,将引用信息放入被引用的对象的region的remembered set中
步骤:
1.初始标记:标记gc roots能直接关联的对象(stop the world)
2.并发标记:标记堆中存活的对象,虚拟机会将对象标记的变化记录在remember set logs中
3.最终标记:为了修正在并发标记的时候用户线程对标记产生变动的一些标记记录,虚拟机将上一阶段的logs合并到各自的region中(stop the world,可以并行)
4.筛选回收:对各个region的回收价值和成本进行排序,优先回收垃圾最多的区域


今日诗词 标题:学习笔记|理解Java虚拟机
作者:ellenbboe
地址:https://ellenbboe.github.io/articles/2019/07/08/1562573236143.html