JVM模块学习

一、JVM类加载机制

加载 >> 验证 >> 准备 >> 解析 >> 初始化 >> 使用 >> 卸载

  • 加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
  • 验证:校验字节码文件的正确性
  • 准备:给类的静态变量分配内存,并赋予默认值
  • 解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块

1、双亲委派机制

为什么要设计双亲委派机制?

  • 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
  • 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性

二、JVM内存模型

三、JVM对象创建

如何分配内存

1、指针碰撞

2、空闲列表

解决并发问题的方法

  • CAS(compare and swap)
  • 本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)

把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在Java堆中预先分配一小块内存。通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。

对象头

对象在内存中存储的布局可以分为3块区域:对象头(Header)、 实例数据(Instance Data)和对齐填充(Padding)

对象逃逸分析

标量替换:分析出来的对象不会逃逸情况下,没有连续的内存分配给对象,会把对象的成员变量拆开分配到内存

四、对象直接进入老年代

1、大对象直接进入老年代,有参数配置

2、长期存活的对象将进入老年代、默认年龄15

3、对象动态年龄判断 一批对象大于s1、s2区域50%,这批对象会直接进入老年代

4、老年代空间分配担保机制

五、JVM对象销毁

1、finalize()方法最终判定对象是否存活,被销毁前会执行finalize()方法

六、垃圾收集器

1、CMS垃圾收集器

老年代收集器,搭配parNew使用,使用的是标记清除算法

  • 初始标记: 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
  • 并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
  • 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(主要是处理漏标问题),这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法(见下面详解)做重新标记。
  • 并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理(见下面三色标记算法详解)。
  • 并发重置:重置本次GC过程中的标记数据。

2、垃圾收集底层算法实现 :三色标记

黑色:gcroot根下的所有对象都被扫描

灰色:gcroot根下的对象没有完全被扫描

白色:还没有被扫描的对象

3、三色标记会产生多标和漏标的问题
  • 多标 : 下次gc再清理就行
  • 漏标 :读写屏障 有两种解决方案: 增量更新 和原始快照 。

增量更新就是有对象引用修改的对象,会放入内存集合中记录,对象一旦新插入了指黑色向白色对象的引用之后, 它就变回灰色对象了。

原始快照就是当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后, 再将这些记录过的引用关系中的灰色对象为根, 重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色

4、记忆集与卡表
解决新生代和老年代跨代引用的问题,会在新生代记录一个数组标识老年代内存块是否dirty,老年代按每页512字节划分内存块,发现dirty会扫描这一整块的内存来判断是否对象是垃圾对象
5、G1垃圾收集器
  • 初始标记(initial mark,STW):暂停所有的其他线程,并记录下gc roots直接能引用的对象,速度很快 ;
  • 并发标记(Concurrent Marking):同CMS的并发标记
  • 最终标记(Remark,STW):同CMS的重新标记
  • 筛选回收(Cleanup,STW):筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿STW时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划,尽量把GC导致的停顿时间控制在我们指定的范围内。不管是年轻代或是老年代,回收算法主要用的是复制算法,这种不会像CMS那样回收完因为有很多内存碎片还需要整理一次,G1采用复制算法回收几乎不会有太多内存碎片。

YoungGC

YoungGC并不是说现有的Eden区放满了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMillis 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时间接近参数 -XX:MaxGCPauseMillis 设定的值,才会触发Young GC

MixedGC

不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC

Full GC

停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的

6、安全点与安全区域

程序需要运行到安全点后才能进行gc

这些特定的安全点位置主要有以下几种:

  1. 方法返回之前
  2. 调用某个方法之后
  3. 抛出异常的位置
  4. 循环的末尾

安全区域 是指在一段代码片段中,引用关系不会发生变化。在这个区域内的任意地方开始 GC 都是安全的。例如线程的sleep操作

七、JVM调优工具

注:pid为进程id

1、jmap 查询内存情况

此命令可以用来查看内存信息,实例个数以及占用内存大小

jmap -histo [pid] 查询对象占用内存情况

jmap -heap [pid] 查询堆的信息

jmap -dump 导出堆的信息

2、jstack [pid] 查询进程情况

例子:

2.1、top -p [pid] 查询进程id

2.2、H来查找线程id

2.3、jstack 19663|grep -A 10 4cd0  19663代表具体的进程id, 4cd0(需要把线程id转成16进制)代表线程id

3、jinfo

3.1、jinfo -flags [pid] 查询jvm参数信息

3.2、jinfo -sysprops 查看java系统参数

4、jstat

4.1、jstat -gc [pid] 可以评估程序内存使用及GC压力整体情况

八、字符串常量池

String的+

编译阶段能确定的字符串,直接引用常量池。编译阶段不能确定的,会生成新的对象。

String的intern方法

String中的intern方法是一个 native 的方法,当调用 intern方法时,如果池已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回池中的字符串。否则,将intern返回的引用指向当前字符串 s1(jdk1.6版本需要将 s1 复制到字符串常量池里)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值