JVM相关知识

JVM主要有堆、方法区、虚拟机栈、本地方法栈、程序计数器组成

线程私有区域:

程序计数器:字节码执行的都有一个编号,就是程序计数器,解释器按这个计数器执行字码,将字节码转换为机器码给cpu运行。其二就是多线程中,也是时间片用完(会用程序计数器记录当前执行到的位置),因为有程序计数器,所以下次再获取时间片时,就可以继续接下去的操作

虚拟机栈:为虚拟机执行Java方法服务。

本地方法栈: 为虚拟机使用到的Native方法服务(本地方法一般就是Native关键字修饰,库里本来就有的方法,有的实现类是用c++实现的,直接调用方法使用就行.总结:java调用非java代码的接口)

线程共有区域(线程共享):

堆:通过new关键字创建的对象都会使用堆内存(它是线程共享的,堆中的对象都要考虑线程安全问题,有垃圾回收机制)

方法区:类信息,类加载器、运行时常量池(当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。)(以前有永久代、现在有元空间)

操作系统内存

直接内存(常见于NIO操作,用于数据缓冲区。分配回收成本较高,但读写性能高。不受JVM内存回收管理)

JVM相关知识_第1张图片

 

一、线程运行诊断

案例一:cpu占用过多

定位

(1)用top命令,定位哪个进程cpu的占用过高

(2)ps H -eo pid,tid,%cpu | grep 进程id(进一步定位那个线程引起的cpu占用过高)

(3)jstack 进程id (将线程转换为16进制,jstack命令得到结果的 nid,找到占用cpu的线程),进一步定位到问题代码的源码行数

案例二:程序运行很长时间没有结果

可能发生死锁

(1)使用jstack 进程id 查看

(2)看到有(Found one Java-level deadlock),定位到相应的源码行数

二、堆内存溢出(OutOfMemoryError:Java heap space)

显示指定堆内存(设置堆内存大小和最大堆内存大小)

-Xms[unit] 初始化堆内存大小

-Xmx[unit] 最大堆内存大小

例如-Xms2G 、-Xmx8G 

堆内存诊断

1、jps工具

查看当前系统中有哪些java进程

2、jmap工具

查看堆内存占用情况

3、jconsole工具

图形界面的、多功能的监测工具,可以连续检测

4、jvisualvm工具

也是图形化界面

jmap heap 进程ip(查看进程的堆内存信息,占用情况)

jconsole(命令行执行,就会弹出一个图形化界面,里面会有相应的监控信息)

案例 垃圾回收后,内存占用任然很高

使用jconsole执行垃圾回收(发现堆内存还是很高)

JVM相关知识_第2张图片

 

jvisualvm(弹出可视化界面)

JVM相关知识_第3张图片

 JVM相关知识_第4张图片

 这样就能查找到占用内存高的对象

 三、元空间内存溢出OutOfMemoryError:Metaspace(1.8以前是叫永久代,1.8以后叫元空间,是方法区内的,类信息占用内存大于最大值会溢出)

永久代:

-XX:MaxPermSize=8m

元空间:

-XX:MaxMetaspaceSize=8m

三、String Table垃圾回收(intern()方法能进常量池)

-Xmx10m -XX:+PrintStringTableStatics -XX:+PrintGCDetails -verbose:gc(显示垃圾回收的信息)

StringTable 性能调优:

1、调整-XX:StringTableSize=桶个数

-Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009(如果读取字符串常量比较多, -XX:StringTableSize=1009把这个池调大,减少哈希冲突,使得性能提升)

2、考虑将字符串对象是否入池

四、直接内存释放原理

1、使用unsafe.allocateMemory()           释放直接内存

2、ByteBuffer的实现类内部,使用Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会由ReferenceHandler线程通过Cleaner的clean方法调用freeMemory来释放直接内存

-XX:+DisableExplicicitGC  显示回收垃圾无效

System.gc() 执行这个是执行一次Full GC,使用上面那个参数,会无效

五、判断垃圾

1、引用计数法:每被引用一次,就计数+1,引用0就可回收

2、可达性分析:(根对象是那种不能被当成垃圾回收的对象,看对象有没有被根对象直接或间接引用,若有就不能当成垃圾回收,否则就可以被垃圾回收)

下载Memory Analyzer(MAT)工具

步骤,控制台输入:

jps  获得进程id

jmap -dump:format=b,live,file=要存储的文件名.bin 进程id

将bin文件在Mat工具中打开

JVM相关知识_第5张图片

 即可查看哪些对象是根对象

JAVA中的四种引用

1、强引用(A1对象)

只要沿着GC Root被引用,那就是强引用不会被回收

JVM相关知识_第6张图片

 

2、弱引用(通过SoftReference),如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

(代码示例如下) 

-Xmx20m -XX:+PrintGCDetails -verbose:gc             打印查看垃圾回收过程

JVM相关知识_第7张图片

3、弱引用的特点是不管内存是否足够,只要发生 GC,都会被回收。

JVM相关知识_第8张图片

 

4、虚引用

必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法(Unafe.freeMemory)释放直接内存

JVM相关知识_第9张图片

 5、终结器引用

无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由Finalizer线程通过终结器引用找到被引用对象并调用它的finalize方法,第二次GC才能回收被引用对象

JVM相关知识_第10张图片

 六、垃圾回收算法

1、标记清除(缺点:会产生垃圾碎片)

2、标记整理

3、复制算法(缺点,内存只能有一半被使用)

JVM相关知识_第11张图片

七、 垃圾回收器

1、串行垃圾回收器(只有一个线程进行GC)

-XX:+UseSerialGC=Serial + SerialOld (新生代用复制算法,老年代用标记-整理算法)

 

JVM相关知识_第12张图片

2、吞吐量优先 

并行垃圾回收器(多个线程一起进行垃圾回收)

-XX:+UseParallelGC  -XX:+UseParallelOldGC   (只要开启其中一个,就会开启另一个)

JVM相关知识_第13张图片

 参数:自适应调整新生代大小、调整垃圾回收时间与总时间比例、最大暂停毫秒数、线程数JVM相关知识_第14张图片

 3、响应时间优先

-XX:+UseConcMarkSweepGC (用户工作线程与垃圾线程一起并行,工作在老年代的垃圾回收器)

与-XX:+UseParNewGC~SerialOld 一起使用的(当垃圾回收器出现问题,它就会退化为SerialOld)

JVM相关知识_第15张图片

 参数JVM相关知识_第16张图片

 

 4、G1垃圾回收器(JDK9被设为默认回收器)

适用场景

同时注重吞吐量和低延迟,默认暂停目标是200ms

超大堆内存,会将堆划分为多个大小相等的Region

整体上是标记+整理算法,两个区域之间是复制算法

-XX:+UseG1GC

 

JVM相关知识_第17张图片

垃圾回收过程

 JVM相关知识_第18张图片

复制到幸存区

 JVM相关知识_第19张图片

 

JVM相关知识_第20张图片

JVM相关知识_第21张图片

 

 去重JVM相关知识_第22张图片

 JVM相关知识_第23张图片

 JVM相关知识_第24张图片

 七、GC调优 

去 官网查看 JVM相关知识_第25张图片

或者用命令行查看

(1)调优领域

1、内存

2、锁竞争

3、cpu占用

4、io

(2)确定目标

1、低延迟还是高吞吐量,选择合适的回收器

2、CMS、G1、ZGC

3、ParallelGC

(3)最快的GC是不发生GC

1、查看FullGC前后的内存占用,考虑下面几个问题

  数据是不是太多

  数据表示是否太臃肿

  是否存在内存泄漏 


八、新生代调优

新生代

内存不是占越大越好,差不多25%-50%之间

幸存区

1、幸存区要大到能保留(当前活跃对象+需要晋升的对象)

2、(因为幸存区是复制算法,需要复制来复制去)

JVM相关知识_第26张图片

老年代调优

JVM相关知识_第27张图片

 案例

案例1 Full GC 和 Minor GC频繁 

新生代内存不足, 当业务高峰期来临,大量对象被创建,幸存区不够,大量对象被放到老年区,导致老年区也满了触发FullGC。

所以增大新生代内存,幸存区内存调大,阈值调高。

案例2 请求高峰期发送FullGC,单次暂停时间特别长(CMS)

重新标记前先对垃圾进行清理

JVM相关知识_第28张图片

 案例3 老年代充裕情况下,发生Full GC(CMS jdk1.7)

1.7及以前 永久代空间不足也会导致Full GC

1.8 元空间不足也会导致Full GC

 

你可能感兴趣的:(jvm)