JVM整体分析篇

JVM的组成部分

在这里插入图片描述

  1. 类装载子系统:将磁盘文件加载到运行时数据区的方法区
  2. 运行时数据区:常说的JVM内存区
  3. 执行引擎:将字节码翻译成底层指令交给CPU执行
  4. 本地库接口:执行引擎需要借助本地库接口实现将指令交给CPU执行

1.类装载子系统

1.1一个类加载到JVM的过程

加载:将磁盘上的字节码文件通过IO读入方法区
验证:验证字节码格式正确性
准备:类的静态变量赋值默认值,分配内存
解析:符号引用替换为直接引用(静态方法执行内存所在的指针)
初始化:对类的静态变量赋值指定值,执行静态代码块

1.2类加载机制

双亲委派机制:类加载时先判断自己有没有加载过,如果没有加载就委派父类执行同样的过程,如果父类没有加载不到在执行自行加载
类加载器

  • 引导类加载器
  • 扩展类加载器
  • app类加载器

类加载器定义:在Java代码的Lancher类中构建了扩展类加载器和app类加载,父子关系也是在这里构建,引导类加载器在C中构建的,所以在Java中体现就是null

1.3为什么设计双亲委派机制

沙箱安全机制:jdk中的类不能被程序员修改
避免类的重复加载:一个类只会被一个加载器加载一次

1.4怎么打破双亲委派机制

重新类加载方法,判断如果是自定义类,就指定自定义类加载器加载,如果不是就使用原来的机制。

2.运行时数据区

在这里插入图片描述

2.1线程私有及共享

私有:栈、本地方法栈、程序计数器
共享:堆、方法区

2.2JVM内存区结构

程序计数器:记录着当前线程的程序执行位置
本地方法栈:虚拟机调用native方法
:局部变量表(存局部变量)、操作数栈(供变量计算)、动态链接(将方法指向内存中真正的方法指针)、方法出口
:几乎所有的对象都在这里分配
方法区:用于存储加载后的类信息、常量、静态变量、编译后的代码

2.3JVM参数设置经验

线程栈:-Xss 每个线程栈的大小,这个参数越小,那么一个线程运行的方法就越少,而虚拟机内部运行的线程数变多
堆:尽量指定堆的大小参数一致避免扩容;分析对象在内存中的分布尽量让对象在年轻代被回收减少full gc次数。
(方法区)元空间:元空间默认为21m,元空间满了之后会触发full gc,所以尽量设置值

3.Java对象的生命周期

3.1.对象的创建

  • 类加载检查:当遇到new关键字时,先判断常量池中能否找到类的符号引用
  • 分配内存:对象所需的内存,在类加载后就能确定。直接为对象分配确定大小的内存
  • 初始化零值:程序能访问的数据,设置对应字段的零值
  • 设置对象头:对象头包含:Mark word,Klass pointer(对象执行类元信息的指针)
  • 执行Init方法:按照程序员的意愿进行属性的赋值,和执行构造方法

3.2.对象大小的计算(64位)

对象头 :

  • (header)Markword:8字节
  • Klass pointer:开启指针压缩(默认开启)4字节

对象数据:

  • 基本数据类型:占数据类型大小
  • 对象、字符串(引用指针):开启指针压缩 4字节
  • 数组:数组长度 4 字节(只要是数组,不区分数组的类型)

对象填充:

  • 保证对象大小是8的倍数,不够时填充

3.3.对象在内存上的分配

对象在栈上分配:栈上分配依赖与逃逸分析和标量替换。

  • 逃逸分析:分析对象作用域,是否只在本地方法使用,无外部引用
  • 标量替换:栈上没有大块连续的内存,所以JVM不会直接创建对象,而是将成员变量分解为被方法使用的变量,存在栈帧寄存器

对象在堆上分配

  • Eden区:正常对象放在Eden区
  • 老年代:对象满足一定条件会分配到老年代

大对象:超过JVM设置的大对象参数值
长期存活对象:经过多次minorGC后存活的对象
对象动态年龄判断:一批对象的总大小超过survivor区域内存大小的50%。那最大年龄的一批对象进入老年代。
老年代空间分配担保:老年代的可用空间小于年轻代所有对象之和

3.4.对象的回收

1.回收那些对象?

  • 引用计数法:对象存在引用,计数+1,引用消失计数减1,不能判断互相引用
  • 可达性分析算法:从GCRoots节点向下搜索引用的对象,未被标记到的是垃圾对象
    2.GC Roots对象:本地方法栈变量、静态变量(局部变量和全局变量即类中定义的变量)
    3.常见的引用类型
  • 强引用:普通变量引用
  • 弱引用:弱引用类型对象包裹
  • 软引用:软引用对象包裹
  • 虚引用:最弱的引用关系

4.垃圾收集器和收集算法

4.1分代收集理论:

  • 根据年龄带不同,选择不同的垃圾收集算法

4.2垃圾收集算法:

  • 标记复制:将存活的对象复制到另一个区域(适合年轻代S0,S1)
  • 标记清楚:标记垃圾对象清除(或者反过来)会产生内存碎片
  • 标记整理:同标记清楚,只是垃圾对象清除后会整理内存空间(适合来年代)

在这里插入图片描述

4.3 Serial:(需要STW)

单线程收集器,进行垃圾收集工作时需要暂停其它所有工作线程

4.4 Serial Old:

和Serial收集器一样,一个用于年轻代,一个用于老年代

4.5 Paralle:(需要STW)

多线程收集器,进行垃圾收集工作时由多个线程进行

4.6 Paralle Old:

和Paralle收集器一样,但是Paralle用于年轻代,Paralle Old用于老年代

4.7ParNew:

多线程收集器,和Paralle相同,但是ParNew可以和CMS收集器配合使用

4.8 CMS:

第一次基本上实现了用户线程和垃圾收集线程同时工作
工作过程:

  • 初始标记:通过GCRoots查找引用对象,速度很快。需要STW
  • 并发标记:通过GCRoots直接引用对象查找其它的引用,可以和用户线程同时运行
  • 重新标记:重新标记是为了修正并发标记阶段用户线程导致的对象变动,主要用到了三色标记
  • 并发清理:用户线程和垃圾收集线程同时运行,回收垃圾对象
  • 并发重置:重置本次GC过程中标记的对象

4.9 垃圾收集算法底层实现:

三色标记:

  • 黑色:对象存活,且所有关系都已经扫描
  • 灰色:至少有一个引用对象未扫描
  • 白色:分析开始阶段都是白色,如果分析结束对象仍然是白色,代表对象是垃圾对象
    漏标对象处理:
  • 增量更新:当一个黑色对象插入新的引用指向一个白色对象时,先将引用记录保存。等并发标记结束后,重新扫描。
  • 原始快照(SATB):当一个灰色对象删除一个指向白色对象时,将要删除的引用记录下来

增量跟新和原始快照中记录的引用,通过写屏障实现
写屏障:在赋值前(赋值对象引用,或赋值引用null)后,加入一些处理

4.10 记忆集和卡表:

当涉及跨代对象引用会收时,不直接扫描跨代区域,而是引入卡表。将存在跨代指针的对象所在卡页(卡表中的元素)表示为1,表示该元素变脏,GC时只收集卡表变脏的元素加入GCRoots中

4.11 G1垃圾收集器:

概念: 面向服务器的垃圾收集器,主要针对多核大容量内存的机器
区域划分: G1保留了年轻代和来年代的概念,但是没有物理上的隔阂。针对于大对象存放,G1增加了Humongous区专门放大对象。G1收集器将堆划分为大小相等的独立区域Region,Region默认大小是堆内存的1/2048,可以使用参数指定。
垃圾收集分类:

  • Young GC: 不同于其它收集器,在Eden区满了就做Young GC,而不是先判断当前Eden区满了后所做GC花费的时间,如果还不够设置的停顿时间,设置更多的Region为Eden区,直到下次满了之后时间也符合。
  • Mixed GC:老年代的堆占有率达到参数设置时触发,回收所有的Young和部分Old以及大对象区,G1垃圾收集器优先做Mixed GC在回收对象时采用复制方法,如果剩余的Region不足以复制,就会产生Full GC.
  • Full GC:停止程序采用单线程标记、清理、压缩
    G1调优关键参数:调节-XX:MaxGCPauseMills GC停顿时间设置

4.12 ZGC:

特点:基于内存布局,暂停时不设置分代
区域划分

  • 小型Region:固定2M,用于放置小于256k的小对象
  • 中型Region:固定32M
  • 大型Region:容量不固定,可以动态变化

4.13 如何选择垃圾收集器?

总结:.如果内存小于100M使用Serial,如果内存小于4G可以使用paralle,内存介于4-8G可以用ParNew+CMS
8G以上可以用G1,几百G以上用ZGC

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值