JVM

164 阅读10分钟
1 JVM 系统架构图

2 类加载器
  几种类加载器
  双清委派机制
  沙箱安全机制

3 Native
  是一个关键字
  有声明,没实现 ,c语言


4 PC寄存器 
  记录了方法之间调用和执行情况,类似排版值日表
  用来存储指向下一条指令的地址,也即将要执行的指令
  代码,它是当前线程所执行的字节码的行号指示器


5 方法区: 线程共享、存在CG
    存放每一个类的结构信息【元数据】(常量池、字段和方法数据、构造函数和普通方法的字节码内容)
    方法区是规范,在不同虚拟机实现是不一样的,最典型的是 永久代(1.7)和元空间(1.8)

    but 
    实例对象在堆内存,与方法区无关

6 栈内存
  栈管运行、堆管存储
  
  不存在垃圾回收机制

  栈保存哪些东西?
    8中基本类型的变量
    +
    对象的引用变量(Person p = new Person() 左边是引用对象在栈里面,右边的实例对象在堆里面)为了保证new出来的person对象,都具有相同的属性和行为信息,所以方法区存了类的结构信息
    +
    实例方法


  方法 = 栈帧 (方法的输入参数、输出参数、和内部变量)

  jave.lang.StackOverflowError   栈溢出(没有递归出口的递归调用,一直压栈,栈也是有容量的)

7 堆、栈、方法区的关系


8 堆(*)
	逻辑上:
		新生区:1.伊甸园区 2.幸存者0区 3.幸存者1区

		老年区

		元空间

	物理上:
		新生区 + 老年区


    new 对象 静态版本:
       伊甸园区有很多new 对象,达到一定的量,会触发YGC

    ```
    
    ```升级版
    


局部变量表:
	静态方法的局部变量表里没有 this 在 index = 0
	构造方法和实例方法局部变量表里 有 index = 0 的 this 站位

	double 和 float 占两个 slot

	可以重复利用slot

	int a = 0
	{
	   int b = 0;
	   b = a + 1
	}
	// 变量c使用之前已经销的变量b占据的slot的位置
	int c = a + 1;


变量的分类
        数据类型区分:
        	基本数据类型
        	引用数据类型
        
        类声明的位置区分:
        	成员变量
        		类变量  (方法之外,static修饰): linkingd的prepare阶段:给类变量默认赋值 ---> initial阶段:给类变量显示赋值即静态代码块赋值
        		实例变量(方法之外,没有static修饰):随着对象的创建,会在堆空间分组实例变量空间并进行默认赋值
        	局部变量(方法之内): 在使用前,必须要显示赋值


操作数据栈
	指令位置  指令 (#包含常量池)
	操作数据栈
	局部变量表
	PC寄存器 (切换指令位置)

	心里有个关系图


动态链接
就是栈帧 中字节码 右边的 # 记录的 指向常量池的方法引用
常量池在 方法区



变量在内部定义且消亡的是线程安全的,如果当初形参和返回出去就不是线程安全了,有可能被其他线程调用和操作,逃逸分析

StringBuilfer 本身是线程不安全的,没有同步机制

StringBuffer 是线程安全的,有同步机制



堆 线程共享
   对象加数组的创建int[] myList = new int[10]

   堆逻辑上三部分:新生代 老年代 元空间

   -Xms10m -Xmx10m 默认(默认四分之一物理机内存 )和最大的堆空间是10MB
   建议初始和最大的设置一样的值

   新生代: 1.伊甸园区 2.幸存者0区 3.幸存者1区
   +
   老年代: 
   = 10MB

   jps
   jstat -gc pid
   或
   -XX:+PrintGCDetails



1.java堆区在JVM启动的时候即被创建,其空间大小也就确定了

2.每一个JVM实例只存在一个堆空间


3.栈帧里的局部变量表 维护这 变量名,存的是对象的引用, 指向 堆里的new 对象,
  方法区保存的是 这些对象的 类及方法实现的数据信息
  一旦 栈帧出栈,指向就没有了,存在堆里的 对象不会马上清除,要等GC操作来决定


tlab
堆是由一个进程的多个线程共享的,存在线程不安全,但是每个线程都会在堆内存开辟属于自己的 tlab 空间,总大小占堆内存的1%

堆的设置参数

逃逸分析
当一个对象在方法中被定义后,对象只在方法内部使用,则没有发生逃逸
当一个对象在方法中被定义后,它被外部方法所引用,则发生逃逸



方法区: 线程共享 类的信息

	jinfo -flag MetaspaceSize pid -- 查看 进程对应的元空间大小

	-XX:MetaspaceSize=21m
	-XX:MaxMetaspaceSize=21m

	OOM

	内部结构

	类信息 + 运行时常量池 + 字符串常量

	运行时常量池:
	数量值
	字符串值
	类引用
	字段引用
	方法引用


垃圾回收
1.什么是垃圾?
	运行程序中没有任何指针指向的对象,这个对象就需要被回收

2.垃圾回收相关的算法
	GC的阶段:标记阶段:
				1.引用计数算法: (java不用,python用)
					1.1.只有有任何一个对象引用就加1,引用失效就减1,为0,就可以被回收
					1.2.优点:实现简单,效率高
					1.3.缺点:无法处理循环引用(对象引用指向线程一个环)的情况,导致内存泄漏

				2.可达性分析算法: (java用)
					2.1.对象直接或间接的可以和 GC Roots(根对象集合) 连接上,连接不上的是可回收的
					2.2.GC Roots 包含哪几类元素呢?
						tips: 由于Root采用栈方式存放变量和指针,所以如果一个指针,它保存了堆内存里面的对象,但是
						自己又不存放在堆内存里面,那它就是一个Root

			 清除阶段:1.标记-清除算法(Mark-Sweep)
			 		  	1.1 当堆中的有效内存空间被耗尽的时候,stop the world (stw)
			 		  	  1.1.1 标记可达对象,递归遍历
			 		  	  1.1.2 回收没有标记为可达对象的对象
			 		  	1.2 效率不高,需要调整整个应用程序,清理出来的空闲内存不是连续的

			         2.复制算法
			         	2.1 将可达对象,复制一份变成有序的(幸存者0,1区就是这样实现的)
			         	2.2 优点:效率高,保证空间的连续性
			         	2.3 缺点:需要两倍的内存空间	

			         3.标记-压缩算法(标记整理)
			         	3.1 标记后,进行可达对象的碎片整理

finalize 机制

	对象生存还是死亡?三个状态
	finalize() 被GC调用,只能被调一次
	可触及的  可达对象
	可复活的  引用都被释放,但是可能在finalize()中复活
	不可触及的 对象的finalize() 被调用,并且没有复活




分代收集算法:具体问题具体分析
    年轻代:区域相对老年代较小, 对象生命周期短,存活率低,回收频繁 -- 复制算法是最快的

    老年代:区域较大,生命周期长,存活率高,回收不及年轻代平凡,有大量存活率高的对象,复制算法明显不合适
           一般由 标记-清除 或 标记-清除和标记-整理混合实现


评估GC的性能指标:吞吐量
   吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)

   暂停时间

   现在的标准:在最大吞吐量优先的情况下,降低停顿时间


java的垃圾回收器有哪些? ***

	7款经典收集器与垃圾分代之间的关系

	Young Gen    Serial GC        Parallel Scavenge GC   ParNew GC
	------------------------------------------------------------------  G1 GC
	  Old Gen    Serial Old GC    Parallel old GC        CMS GC

	.新生代收集器: Serial(串行)、ParNew(并行)、Parallel Scavenge(并行拾荒)
	.老年代收集器: Serial Old、Parallel old、CMS
	.整堆收集器:  G1

	注意:
	1. jdk8  是 Parallel Scavenge GC 和 Parallel old GC 组合

	2. 单cpu clien 串行要好于并行的GC,不用切换线程,吞吐量更大

	3. 查看是否使用某个垃圾回收器
	jinfo -flag UseParallelGC  pid
	jinfo -flag UseParallelOldGC  pid


*****  *****
Serial GC 串行回收
 0. 新生代
 1. 采用复制算法、串行回收、stop-the-world (stw) 停掉用户线程
 2. 优点:运行在client模式下,简单高效
 	缺点:stw
 3. 指定Serial GC : -XX:+UseSerialGC
    新生代老年代都是使用的串行回收器
 4. java web 应用是不会采用这种串行GC

Serial old GC
 0. 老年代
 1. 采用标记-压缩算法、串行回收、stop-the-world (stw) 停掉用户线程
*****  *****


*****  不太使用了*****
ParNew Serial的多线程版本
0. 新生代
1. 采用复制算法、并行回收、stop-the-world (stw) 停掉用户线程
2. 运行在server模式下
3. 低版本,老年代搭配 Serial old GC
4. 尴尬的定位,高版本没有 老年代的搭档了
*****  *****


*****  Java 8 默认*****
Parallel Scavenge GC 吞吐量优先
0. 新生代
1. 采用复制算法、并行回收、stop-the-world (stw) 停掉用户线程
2. 可控制的吞吐量


Parallel old GC
 0. 老年代
 1. 采用标记-压缩算法、并行回收、stop-the-world (stw) 停掉用户线程



 参数配置
 -XX:+UseParallelGC     手动开启新生代为 并行收集器
 -XX:+UseParallelOldGC  手动开启老年代为 并行收集器

 以上参数 ,相互生效激活

 -XX:ParallelGCThreads  最好与CPU数量相等

 -XX:GCTimeRatio 垃圾收集时间占总时间的比例(=1 /(n + 1)) (0-100) 默认n=99 ,垃圾回收时间不超过1%
 -XX:+UseAdaptiveSizePolicy 具有自适应调节策略
*****  *****


***** JDK 14 被删除 *****
CMS GC  低延迟
0. 老年代
1. 并发收集器,第一次实现让垃圾收集线程与用户线程同时工作
2. 采用标记-清除算法,并且也会 stop-the-world (stw) 
3. 适用 看重服务器响应数据的,希望系统停顿时间短的
4. 初始标记(stw)标记GC Roots 直接关联到的对象->并发标记(和用户线程同时工作)遍历整个对象图->重新标记(stw)要停止用户线程去修复->并发清理(和用户线程同时工作)

5. 优点:并发,低延迟
   缺点:使用标记-清除算法,就会有内存碎片,占用用户线程,吞吐量降低
   为什么不使用标记-压缩算法呢?
   因为,标记压缩算法要重新指定对象内存,用户线程就得暂停,出现 stw

4. 只能跟ParNew GC 或 Serial GC 中的一个,原因和其他的底层架构不兼容

5. 提示下,可以在CMS GC 后,对内存进行压缩整理



参数配置
-XX:+UseConcMarkSweepGC           表明老年代使用CMS GC ,新生代默认变成 ParNew GC
-XX:CMSInitiatingOccupanyFraction 设置堆内存使用率的阈值
*****  *****






总结:什么情况下使用哪种GC

最小地使用内存和并行开销使用 Serial GC + Serial Old GC
最大应用程序的吞吐量使用Parallel GC + Parallel Old GC
最小化GC的中断时间或停顿时间使用CMS GC + ParNew GC



*****  *****
C1 GC 区域化分代式
适用:新生代和老年代
0. 目标:延时可控的情况下,获得尽可能高的吞吐量
1. 特点
  1.0.并行与并发
  1.1.分代回收, 将堆空间分成若干个区域(Region)
  1.2.空间整合, Region之间是复制算法,整体上可以看成 标记-压缩
  1.3.可预测的停顿时间模型
  	1.3.1.由于分区的原因,G1可以只选取部分区域进行内存回收


G1回收器的参数设置
	-XX:+UseG1GC 		 手动指定使用G1收集器
	-XX:G1HeapRegionSize 设置每个Region的大小,值是2的幂次方 1 - 32MB
	-XX:MaxGCPauseMillis 设置期望到达的最大GC停顿时间
	-XX:ParallelGCThread 设置STW时GC线程数的值
	-XX:ConcGCThreads    设置并发标记的线程数
	-XX:InitiatingHeapOccupancyPercent 设置触发并发GC周期的java堆占用率阈值,默认是45

G1的设计就是简化JVM性能调优
第一步:开始G1垃圾收集器
第二步:开启堆的最大内存
第三步:设置最大的停顿时间


G1中提供了三种垃圾回收模式:YoungGC, Mixed GC 和 Full GC,在不同条件下会触发
*****  *****









    ```