jvm
2020-08-17 11:37:56 3 举报
jvm学习资料整理,边学边整理边练习,笔记
作者其他创作
大纲/内容
这个 写屏障 不等于内存屏障。
基础概念
3.3 标记压缩-mark compact
heapdump + jhat分析
案例8:如果有一个系统,内存一直消耗不超过10%,但是观察GC日志,发现FGC总是频繁产生,会是什么引起的?
GC何时触发
(YGC-》MixedGC-》FullGC)
调优前的基础概念
如果stack空间不够分配,则到伊甸区分配
多线程版本的GC收集器,使用compacting算法。缺点:会进入\"Stop-The World\"状态。Parallel Old是Serial Old的并行版本
1、对象头(markword),8字节;2、ClassPointer指针,-XX:+UseCompressedClassPointers 为4字节,不开启为8字节; 3、数组长度,4字节;4、数组数据5、Padding对齐,长度为8的倍数;
JNI指针
S0
customclassLoader
查看GC情况
3.2 拷贝算法-copying
回收后
public class LambdaGC { public static void main(String[] args) { for(;;) { I i = C::n; } } public static interface I { void m(); } public static class C { static void n() { System.out.println(\"hello\"); } }}
2020-08-11 15:51:32Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.91-b14 mixed mode):\"Attach Listener\" #60 daemon prio=9 os_prio=31 tid=0x00007f995f012000 nid=0x340b waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE\"pool-1-thread-50\" #59 prio=5 os_prio=31 tid=0x00007f995c88b000 nid=0x15103 waiting on condition [0x0000700005c1b000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007bf800780> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088) at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809) at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)...
代码块一:Object o = null;for(int i=0; i<100; i++) { o = new Object(); //业务处理}代码块二:for(int i=0; i<100; i++) { Object o = new Object();}结论:代码块一更优。100次循环里,【代码块一】只有一个对象o有引用,另外99个引用会释放让gc回收。而【代码块二】在循环结束前会有100个对象引用。
记录了其他Region中的对象到本Region的引用。RSet的价值在于使得垃圾收集器不需要扫描整个堆找到谁引用了当前分区中的对象,只需要扫描RSet即可。
OOM产生的原因多种多样,有些程序未必产生OOM,但会不断FGC(CPU飙高,但内存回收特别少)
推导:1、高吞吐量 -》 gc次数少 -》每次gc耗时长(STW)-》暂停时间长2、低暂停时间 -》gc次数多 -》每次gc耗时短(STW)-》吞吐量低(吞吐量 = 应用程序时间 / 总时间(应用+gc))
样例代码,有可能造成方法区溢出。for循环会创建很多个不同的匿名内部类。
arthas
加载类过程
1、当cpu执行存储指令时,它会首先试图将数据写到离cpu最近的L1_cache;2、如果此时cpu出现L1未命中,则会访问下一级缓存。速度上L1_cache基本能和cpu持平,其他的均明显低于cpu,L2_cache的速度大约比cpu慢20-30倍;3、若L2_cache不命中,需要更多的周期去主存读取。4、其实在L1_cache未命中以后,cpu就会使用一个另外的缓冲区,叫做合并写存储缓冲区。这一技术称为合并写入技术。在请求L2_cache缓存行的所有权尚未完成时,cpu会把待写入的数据写入到合并写存储缓冲区,该缓冲区大小和一个cache line大小,一般都是64字节。这个缓冲区允许cpu在写入或者读取该缓冲区数据的同时继续执行其他指令,这就缓解了cpu写数据时cache miss时的性能影响。
方法里调方法,嵌套很深。一个线程拥有一个jvm栈,一个方法一个栈帧,方法调方法会在栈里压相应数量的栈帧,可能导致OOM。
句柄池
stack
jstat -gc 4655 500jstat -gc pid 每隔500毫秒打印GC的情况。(一般不常用)
top
1、对象头(markword),8字节;2、ClassPointer指针,-XX:+UseCompressedClassPointers 为4字节,不开启为8字节; 3、实例数据引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 Oops Ordinary Object Pointers4、Padding对齐,长度为8的倍数;
G1中的MixedGC
* 调优,从业务场景开始,没有业务场景的调优都是耍流氓* 无监控(压力测试,能看到结果),不调优* 步骤: 1. 熟悉业务场景(没有最好的垃圾回收器,只有最合适的垃圾回收器) 1. 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应) 2. 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS] 2. 选择回收器组合 3. 计算内存需求(经验值 1.5G 16G) 4. 选定CPU(越高越好) 5. 设定年代大小、升级年龄 6. 设定日志参数 1. -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause 2. 或者每天产生一个日志文件 7. 观察日志情况
· concurrent mark sweep老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms)CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收四个阶段:1、initial mark,标记根对象,会STW2、concurrent mark,跟工作线程同时进行,继续标记跟对象的子节点。3、remark,重新标记,会STW4、concurrent sweep,可能会产生浮动垃圾,留着下个轮回再处理。
A
主内存
3、吞吐量 VS 暂停时间
initializing
· 一个JVM线程有一个私有的JVM栈,在创建线程的时候同时创建。· 每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。· 调用一个方法,就会向栈中压入一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出。
· 引用计算(Reference Count)· 根可达算法(Root Searching)
dashboard 观察系统情况
什么情况下,对象会在栈上分配呢?
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags \\-XX:+PrintGC -XX:+UseConcMarkSweepGC \\-XX:+PrintCommandLineFlags benjamin.HelloGC
Card (512 Byte)
Card Table由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为【Dirty】,下次扫描时,只需要扫描【Dirty Card】在结构上,Card Table用BitMap来实现。对于HotSpot JVM,使用了卡标记(Card Marking)技术来解决老年代到新生代的引用问题。具体是,使用卡表(Card Table)和写屏障(Write Barrier)来进行标记并加快对GC Roots的扫描。
new / young 新生代GC: MinorGC/YGC
MethodArea
3、Java体系结构
Parallel Scavenge的一个变种,用于和CMS配合使用。如CMS在某个阶段的时候 ,parNew会同时运行。
Region可以是 年轻代区域,也可以是 老年代区域。虽然分区使得内存分配不再要求紧凑的内存空间,但G1依然使用了分代的思想。与其他垃圾收集器类似,G1将内存在逻辑上划分为年轻代和老年代,其中年轻代又划分为Eden空间和Survivor空间。但年轻代空间并不是固定不变的,当现有年轻代分区占满时,JVM会分配新的空闲分区加入到年轻代空间。整个年轻代内存会在初始空间-XX:G1NewSizePercent(默认整堆5%)与最大空间-XX:G1MaxNewSizePercent(默认60%)之间动态变化,且由参数目标暂停时间-XX:MaxGCPauseMillis(默认200ms)、需要扩缩容的大小以及分区的已记忆集合(RSet)计算得到。当然,G1依然可以设置固定的年轻代大小(参数-XX:NewRatio、-Xmn),但同时暂停目标将失去意义。
线程pid转十六进制定位jstack日志
frame
打破上述两个条件之一即可1、incremental update——增量更新,关注引用的增加,把黑色重新标记为灰色,下次重新扫描属性。2、SATB snapshot at the beginning——关注引用的删除。当B->D消失时,要把这个引用推到GC的堆栈,保证D还能被GC扫描到。
Card Page基于卡表(Card Table)的设计,通常将堆空间划分为一系列2次幂大小的卡页(Card Page)。卡表(Card Table),用于标记卡页的状态,每个卡表项对应一个卡页。HotSpot JVM的卡页(Card Page)大小为512字节,卡表(Card Table)被实现为一个简单的字节数组,即卡表的每个标记项为1个字节。当对一个对象引用进行写操作时(对象引用改变),写屏障逻辑将会标记对象所在的卡页为dirty。
Java 类加载器
L1 高速缓存
8 byte
对象 new T()
加载classpath指定内容
JDKJDK(Java Development Kit,Java开发工具包)
堆内存分配情况
无缓存行对齐
cms算法
伪共享和缓存行
吞吐量是指用户应用程序线程用时占程序总用时的比例。例如,吞吐量99/100意味着100秒的程序执行时间中用户应用程序线程运行了99秒, 而在这段时间内GC线程只运行了1秒。
D
tenured(终身)
preparation静态成员变量赋默认值
3、对象头包括什么
4、Parallel Old
Garbage Collector
Heap
1、什么是垃圾
public class Cacheline_nopadding { @sun.misc.Contended public static class T{ //8字节 private volatile long x = 0L; } private static T[] arr = new T[2]; static { arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(()->{ for(long i = 0;i < 1000_0000L;i++){ //volatile的缓存一致性协议MESI或者锁总线,会消耗时间 arr[0].x = i; } }); Thread thread2 = new Thread(()->{ for(long i = 0;i< 1000_0000L;i++){ arr[1].x = i; } }); long startTime = System.nanoTime(); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(\"总计消耗时间:\"+(System.nanoTime()-startTime)/100_000); }
humongous object
代码
总计消耗时间:3381
自定义classloader
NativeMethodStacks
案例3:tomcat max-http-header-size过大问题
5%~60%一般不用手工指定,也不要手工指定。因为这是G1预测停顿时间的基准。G1能自动动态调整新老年代比例,控制停顿时间。
2、对象在内存中的存储布局
由于RSet的存在,那么每次给对象赋值引用的时候,就得做一些额外的曹邹。指的是在RSet中做一些额外的记录(在GC中被称为写屏障)
2、JRE、JDK和JVM的关系
分区 Region
运维人员
负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名、ClassLoader完成类的加载。因此,标识一个被加载了的类:类名 + 包名 + ClassLoader实例ID。
一个对象从出生到消亡
没有遍历到的节点
标题 1
标题 2
标题 3
标题 4
标题 5
Card 1
Card 2
...
Card n
调优,从规划开始
面试题:关于对象
· 线程私有小对象· 无逃逸· 支持标量替换
加载lib/rt.jar charset.jar等核心类,由C++实现。
定位CPU100%问题流程(命令行)
jps
普通对象
CPU 2
服务器触发报警
ParallelScavenge
public class Cacheline_nopadding { public static class T{ //8字节 private volatile long x = 0L; } private static T[] arr = new T[2]; static { arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(()->{ for(long i = 0;i < 1000_0000L;i++){ //volatile的缓存一致性协议MESI或者锁总线,会消耗时间 arr[0].x = i; } }); Thread thread2 = new Thread(()->{ for(long i = 0;i< 1000_0000L;i++){ arr[1].x = i; } }); long startTime = System.nanoTime(); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(\"总计消耗时间:\"+(System.nanoTime()-startTime)/100_000); }}
标量替换:若一个对象被证明不会被外部访问,并且这个对象可以被拆解成若干个基本类型的形式,那么当程序真正执行的时候可以不创建这个对象,而是采用直接创建它的若干个被这个方法所使用到的成员变量来代替,将对象拆分后,除了可以让对象的成员变量在栈上分配和读写之外,还可以为后续进一步的优化手段创造条件。
RSet 与 赋值的效率
自己已经标记,fields都标记完成
2、暂停时间
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags \\-XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps \\-XX:+PrintGCCause benjamin.HelloGC
region
a class life cycle
常量池
或
调用类初始化代码 <clinit>,给静态成员变量赋初始值
并发标记算法:三色标记法
Program Counter
如果一个cpu在执行的时候需要访问的内存都不在cache中,cpu必须要通过内存总线到主存中取,那么在数据返回到cpu这段时间内(这段时间大致为cpu执行成百上千条指令的时间,至少两个数据量级)干什么呢? 答案是cpu会继续执行其他的符合条件的指令。这也是导致cpu乱序执行指令的根源之一。
静态变量
eg.waiting on <0x0000000088ca3310> (a java.lang.Object)假如有一个进程中100个线程,很多线程都在waiting on <xx> ,一定要找到是哪个线程持有这把锁怎么找?——搜索jstack dump的信息,找<xx> ,看哪个线程持有这把锁RUNNABLE
存活对象
指定pn cms+so
1
L2 高速缓存
对于现代cpu而言,性能瓶颈则是对于内存的访问。cpu的速度往往都比主存的高至少两个数量级。因此cpu都引入了L1_cache与L2_cache,更加高端的cpu还加入了L3_cache。
1、句柄池
未使用
JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。
加载拓展jar包 jre/lib/ext/*.jar,或由-Djava.ext.dirs指定
survivor0
thread定位线程问题(同 jstack 指令)
Java VS C++
* -XX:+UseG1GC* -XX:MaxGCPauseMillis 建议值,G1会尝试调整Young区的块数来达到这个值* -XX:GCPauseIntervalMillis ?GC的间隔时间* -XX:+G1HeapRegionSize 分区大小,建议逐渐增大该值,1 2 4 8 16 32。 随着size增加,垃圾的存活时间更长,GC间隔更长,但每次GC的时间也会更长 ZGC做了改进(动态区块大小)* G1NewSizePercent 新生代最小比例,默认为5%* G1MaxNewSizePercent 新生代最大比例,默认为60%* GCTimeRatio GC时间建议比例,G1会根据这个值调整堆空间* ConcGCThreads 线程数量* InitiatingHeapOccupancyPercent 启动G1的堆空间占用比例
Method Area
解决漏标
· 在任意时刻,一个处理器只会执行一条线程中的指令。因此,为了线程切换后能够恢复到正确的执行位置,每条线程需要有一个独立的程序计数器(线程私有)。· 如果线程正在执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址;· 如果正在执行的是Native方法,则这个计数器为空。
保留空间
verification验证文件是否符合JVM规定
G1算法
G1
java10以前是串行 FullGC,之后是并行 FullGC。
Epsilon
1、方法逃逸
DirectMemory
5、ParNew
缓存行:64字节
JVM
watch - watch method
max-http-header-size: 10000000tomcat的线程在处理过程中分配了10M的buffer在堆上,每个链接都分配10M导致OOM。默认4k就好。
确认占用cpu进程top指令,得到pid观察到问题:内存不断增长 CPU占用率居高不下
jad反编译使用场景:1、动态代理生成类的问题定位2、第三方的类(观察代码)3、版本问题(确定自己最新提交的版本是不是被使用)
案例
jstack pid
这就是垃圾!
一个缓存行64字节,一个long字段8字节,因此一个缓存行可以存放8个long字段。
现代CPU的数据一致性实现 = 缓存锁(MESI ...) + 总线锁读取缓存以cache line为基本单位,目前64bytes
S1
Java8对伪共享的解决,sun.misc.Contended注解:注意:默认情况下此注解是无效的,需要在JVM启动时设置-XX:-RestrictContended
CPU中每个缓存行(caceh line)使用4种状态进行标记(使用额外的两位(bit)表示):M: 被修改(Modified)E: 独享的(Exclusive)S: 共享的(Shared)I: 无效的(Invalid)https://www.cnblogs.com/z00377750/p/9180644.html
GC常用参数
优点:单线程精简的GC实现,无需维护复杂的数据结构,初始化简单,是client模式下JVM默认选项。最古老的GC。缺点:会进入\"Stop-The World\"状态。
package com.mashibing.jvm.gc;import java.math.BigDecimal;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;/** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */public class T15_FullGC_Problem01 { private static class CardInfo { BigDecimal price = new BigDecimal(0.0); String name = \"张三\
一般是运维团队首先受到报警信息(CPU Memory)
JVM Runtime
当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种行为就叫做 方法逃逸。
MixedGC的过程
1、对象的创建过程
1、建立引用A->D2、删除引用B->D此时,造成漏标D,导致D被回收。
2、线程逃逸
缓存行对齐方案
num #instances #bytes class name---------------------------------------------- 1: 471900 33976800 java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask 2: 471926 18877040 java.math.BigDecimal 3: 471900 15100800 benjamin.T15_FullGC_Problem01$CardInfo 4: 471900 11325600 java.util.Date 5: 471900 11325600 java.util.concurrent.Executors$RunnableAdapter 6: 314135 10052320 java.util.concurrent.locks.AbstractQueuedSynchronizer$Node 7: 471900 7550400 benjamin.T15_FullGC_Problem01$$Lambda$2/1826771953 8: 11210 5317992 [I 9: 1 2396432 [Ljava.util.concurrent.RunnableScheduledFuture; 10: 3115 487400 [Ljava.lang.Object; 11: 1649 136888 [C 12: 745 85312 java.lang.Class 13: 1577 37848 java.lang.String 14: 13 25360 [B 15: 58 21808 java.lang.Thread 16: 185 10360 java.lang.invoke.MemberName 17: 267 8544 java.util.concurrent.ConcurrentHashMap$Node
RuntimeConstantPool
run engine
Java
Global Card Table
1、吞吐量
6、Object o = new Object()在内存中占多少字节
GC 算法
多线程版本的GC收集器,使用copying算法。缺点:会进入\"Stop-The World\"状态。
MESI:CPU缓存一致性协议
漏标原因:并发标记阶段中, 1、建立引用A->D2、删除引用B->D
Runtime Data Area
总计消耗时间:1428
T.class文件
jps定位具体java进程,得到pid
关键:1、synchronized锁对象时,对象头中有3位标志位(1位是否偏向锁标志 + 2位锁标志位),用于标识对象锁升级的当前锁状态;2、记录对象GC情况;
1、灰色-》白色引用消失时,如果没有黑色指向白色,引用依然会被push到堆栈。2、下次扫描时拿到这个引用,由于RSet的存在,不需要扫描整个堆去查找指向白色的引用,效率比较高。SATB配合RSet,浑然天成。
CPU 1
jvm观察jvm信息(同 jinfo 指令)
Card Table & Card
B
1、扩内存。2、提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)3、降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)
案例6:比较下面代码的异同,哪个更优
关键
缓存行对齐
CMS
G1 算法
使用空间
结论:通过逃逸分析技术可以判断一个对象不会逃逸到方法或者线程之外。根据这一特点,就可以让这个对象在栈上分配内存,对象所占用的内存空间就可以随帧栈出栈而销毁。在一般应用中,不会逃逸的局部对象所占比例很大,如果能使用栈上分配,那么大量的对象就会随着方法的结束而自动销毁了,垃圾收集系统的压力就会小很多。
为什么G1用SATB
计算单元寄存器
System.gc()
survivor1
Remembered Set(RSet)
1.高吞吐量最好,因为这会让应用程序的最终用户感觉只有应用程序线程在做“生产性”工作。 直觉上,吞吐量越高程序运行越快。2.低暂停时间最好,因为从最终用户的角度来看不管是GC还是其他原因导致一个应用被挂起始终是不好的。 这取决于应用程序的类型,有时候甚至短暂的200毫秒暂停都可能打断终端用户体验。 因此,具有低的最大暂停时间是非常重要的,特别是对于一个交互式应用程序。3.不幸的是”高吞吐量”和”低暂停时间”是一对相互竞争的目标(矛盾)。
相当于 CMS。-XX:InitiatingHeapOccupancyPercent默认指45%当O超过这个值时,启动MixedGC
逃逸分析的主要作用就是分析对象作用域
YGC Eden空间不足 多线程并行执行FGC Old空间不足 System.gc()
1. java -XX:+PrintFlagsInitial 默认参数值2. java -XX:+PrintFlagsFinal 最终参数值3. java -XX:+PrintFlagsFinal | grep xxx 找到对应的参数4. java -XX:+PrintFlagsFinal -version |grep GC
如果G1产生FGC,你应该做什么?
t
并发标记阶段
线程共享区域
数组对象
resolution
什么是调优
案例7:重写finalize引发频繁GC
L3 缓存
G1采用了分区(Region)的思路,将整个堆空间分成若干个大小相等的内存区域,每次分配对象空间将逐段地使用内存。因此,在堆的使用上,G1并不要求对象的存储一定是物理上连续的,只要逻辑上连续即可;每个分区也不会确定地为某个代服务,可以按需在年轻代和老年代之间切换。启动时可以通过参数-XX:G1HeapRegionSize=n可指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区。
JVM Stacks
甚至该对象还可能被外部线程访问到,例如赋值被类变量或可以在其他线程中访问的实例变量,称为 线程逃逸。
灰色:自身被标记,成员变量未被标记。
Class Loader(类加载器):用于装载.class文件。Execution Engine(执行引擎):用于执行字节码或者本地方法。运行时数据区:方法区、堆、java栈、pc寄存器、本地方法栈。
跨越多个region
3、Parallel Scavenge
类加载器加载顺序
指向
黑色:自身和成员变量均已标记完成。
include
JVM调优
Eden
CPU合并写技术
同 Serial。Serial和Serial Old都是单线程进行GC,特点就是GC时暂停所有应用线程。
Mark Compact或Mark Sweep
JREJRE(Java Runtime Environment, Java运行环境)
定位CPU100%问题流程(arthas在线排查工具)
3.1 标记清除-mark sweep
问题:两个线程分别对两个变量(刚好在同一个缓存行)分别进行读写。线程间分别争夺缓存行的写权限,争夺失败者把自己对应的缓存行置为失效。造成效率问题。
executionengine
java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags \\-XX:+PrintGC benjamin.HelloGC
heap space
Run-Time Constant Pool
· Java堆是Java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享。· Java对象实例以及数组都在堆上,分配运行时数据区内存。
案例5:栈溢出问题
硬件层数据一致性
ParNew
redefine 热替换使用场景:1、目前有些限制条件:只能改方法实现(方法已经运行完成),不能改方法名, 不能改属性m() -> mm()
线程CPCprogram counterVMSvirtual machine stacksNMSnative method stacks
jstack 定位线程状况,重点关注:WAITING BLOCKED
extension classLoader
Shenandoah
C
HotSpot参数分类
Old
appclassLoader
当后续的写操作需要修改相同的缓存行时,这些缓冲区变得非常有趣。在将后续的写操作提交到L2缓存之前,可以进行缓冲区写合并。 这些64字节的缓冲区维护了一个64位的字段,每更新一个字节就会设置对应的位,来表示将缓冲区交换到外部缓存时哪些数据是有效的。当然,如果程序读取已被写入到该缓冲区的某些数据,那么在读取缓存数据之前会先去读取本缓冲区的。经过上述步骤后,缓冲区的数据还是会在某个延时的时刻更新到外部的缓存(L2_cache).如果我们能在缓冲区传输到缓存之前将其尽可能填满,这样的效果就会提高各级传输总线的效率,以提高程序性能。
classLoader
Method AreaPermMeta Space
2、Serial Old
1、栈,分配到栈;2、堆,栈空间不够,且对象是一个大对象,分配到堆(FGC);3、线程本地,小对象,分配到eden,进行GC过程,年龄到了就到FGC,年龄没到就不断循环GC过程;
案例1:硬件升级系统反而卡顿的问题
OldGC收集器组合
每个region,1MB~32MB
Parallel常用参数
JVMJVM(Java Virtual Machine, Java虚拟机)
回收前
标记后
jmap -histo日志
Copying
线程栈变量
通用参数
GC
5、对象怎么分配
sc - search class
.java类
3、常见的垃圾回收算法
Serial
暂停时间是指一个时间段内应用程序线程让与GC线程执行而完全暂停。 例如,GC期间100毫秒的暂停时间意味着在这100毫秒期间内没有用户应用程序线程是活动的。
G1常用参数
Young GC收集器组合
在完成concurrent mark后,remark过程中,黑色指向了白色,如果不对黑色重新扫描,则会漏标。会把白色D对象当做没有新引用指向,从而回收掉。并发标记过程中,Mutator删除了所有从灰色到白色的引用,会产生漏标,此时白色对象应该被回收。
* -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间* -XX:+UseTLAB 使用TLAB,默认打开* -XX:+PrintTLAB 打印TLAB的使用情况* -XX:TLABSize 设置TLAB大小* -XX:+DisableExplictGC System.gc()不管用 ,FGC* -XX:+PrintGC* -XX:+PrintGCDetails* -XX:+PrintHeapAtGC* -XX:+PrintGCTimeStamps* -XX:+PrintGCApplicationConcurrentTime (低) 打印应用程序时间* -XX:+PrintGCApplicationStoppedTime (低) 打印暂停时长* -XX:+PrintReferenceGC (重要性低) 记录回收了多少种不同引用类型的引用* -verbose:class 类加载详细过程* -XX:+PrintVMOptions* -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial 必须会用* -Xloggc:opt/log/gc.log* -XX:MaxTenuringThreshold 升代年龄,最大值15* 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 ... 这些不建议设置
linking
* -XX:SurvivorRatio* -XX:PreTenureSizeThreshold 大对象到底多大* -XX:MaxTenuringThreshold* -XX:+ParallelGCThreads 并行收集器的线程数,同样适用于CMS,一般设为和CPU核数相同* -XX:+UseAdaptiveSizePolicy 自动选择各区大小比例
...
2、如何定位垃圾
free region
Serial Old
依据问题2,且JVM开启了-XX:+UseCompressedClassPointers,占16字节。16字节 = markword 8(固定) + classPointer 4(压缩) + 数据区 0(空) + padding 4(补齐)
漏标问题
- 手工处理垃圾- 忘记回收 内存泄露- 回收多次 非法访问-开发效率低,执行效率高
总计消耗时间:1289
jmap - histo 4655 | head -20jmap -histo pid | head 前20条列出产生最多对象的排名
PS GC日志详解
8
案例4:lambda表达式导致方法区溢出问题(MethodArea / Perm Metaspace)
默认ps po
loading
一组可被回收的分区的集合。在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden空间、survivor空间、或者老年代。CSet会占用不到整个堆空间的1%大小。
将类、方法、属性等符号引用解析为直接引用常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
G1的内存区域不是固定的 E 或者 O
· 标记清除-mark sweep位置不连续,产生碎片,效率偏低(两遍扫描)· 拷贝算法-copying没有碎片,浪费空间。需要调整对象引用· 标记压缩-mark compact没有碎片,效率偏低(两遍扫描,指针需要调整)
自己标记完成,还没来得及标记fields
Region
ZGC
6、CMS
1、请解释一下对象的创建过程?2、对象在内存中的存储布局?3、对象头具体包括什么?4、对象怎么定位?5、对象怎么分配?6、Object o = new Object()在内存中占多少字节?
存放指令位置虚拟机的运行,类似于这样的循环:while( not end ) {\t取PC中的位置,找到对应位置的指令;\t执行该指令;\tPC ++;}
超过单个region的50%
2、直接指针
没有包含的功能:jmap
· 类文件被类装载器装载进来之后,类中的内容(比如变量,常量,方法,对象等这些数据存储的地方。
清除后
综上所述,在设计(或使用)GC算法时,我们必须确定我们的目标:一个GC算法只可能针对两个目标之一(即只专注于最大吞吐量或最小暂停时间),或尝试找到一个二者的折衷。
JVM Stacks
C++
eden(伊甸)
堆内存逻辑分区(不适用不分代垃圾收集器)
ParallelOld
jstack 日志
确认占用cpu进程下的线程top -Hp pid观察进程中的线程,哪个线程CPU和内存占比高
小米云,HBase同步系统,系统通过nginx访问超时报警,最后排查,C++程序员重写finalize引发频繁GC问题。为什么C++程序员会重写finalize?C++需要手动释放内存,因此想当然类比java finalize能做到释放内存。(new delete)当finalize耗时比较长(200ms),导致GC不及时,导致频繁触发GC。
.class类
CMS常用参数
used region
分区概念
可回收
· 方法区是各个线程共享的内存区域,在虚拟机启动时创建。· 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。· 虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非堆),目的是与Java堆区分开来。· 当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。
bootstrap classLoader
1. Perm Space (<1.8) 字符串常量位于PermSpace FGC不会清理 大小启动的时候指定,不能变2. Meta Space (>=1.8) 字符串常量位于堆 会触发FGC清理 不设定的话,最大就是物理内存
1、什么是JVM
* -XX:+UseConcMarkSweepGC* -XX:ParallelCMSThreads CMS线程数量* -XX:CMSInitiatingOccupancyFraction 使用多少比例的老年代后开始CMS收集,默认是68%(近似值),如果频繁发生SerialOld卡顿,应该调小,(频繁CMS回收)* -XX:+UseCMSCompactAtFullCollection 在FGC时进行压缩* -XX:CMSFullGCsBeforeCompaction 多少次FGC之后进行压缩* -XX:+CMSClassUnloadingEnabled* -XX:CMSInitiatingPermOccupancyFraction 达到什么比例时进行Perm回收* GCTimeRatio 设置GC时间占用程序运行时间的百分比* -XX:MaxGCPauseMillis 停顿时间,是一个建议时间,GC会尝试用各种手段达到这个时间,比如减小年轻代
class
1. 根据需求进行JVM规划和预调优2. 优化运行JVM运行环境(慢,卡顿)3. 解决JVM运行过程中出现的各种问题(OOM)
JMM
top -Hp pid
· 没有任何引用指向的一个对象,或多个对象(循环引用)。
案例2:线程池不当运用产生OOM问题
ProgramCounter
新老年代比例
GC root
.javacJava Compiler
jmap - histo pid | head -n
线程BPCprogram counterVMSvirtual machine stacksNMSnative method stacks
白色:未被标记的对象。
G1的 FullGC
S0~S1之间的复制年龄超过限制时,进入Old区通过参数:-XX:MaxTenuringThreshold配置
1、Serial
4、对象怎么定位T t = new T()
old 老年代MajorGC/FGC
线程APCprogram counterVMSvirtual machine stacksNMSnative method stacks
3
1、初始标记 STW2、并发标记3、最终标记 STW(重新标记)4、筛选回收 STW(并行)
package benjamin;import java.util.LinkedList;import java.util.List;/** * @author Ben Li. * @since: 2020/8/11 9:46 上午 */public class HelloGC { public static void main(String[] args) { System.out.println(\"Hello GC!\"); List list = new LinkedList(); for (; ; ) { byte[] b = new byte[1024 * 1024]; list.add(b); } }}
- GC 处理垃圾- 开发效率高,执行效率低
Collection Set(CSet)
0 条评论
下一页