`

浅析HotSpot内存管理与分析工具

    博客分类:
  • java
阅读更多

4月15号在公司给大家分享了下Hotspot的内存管理和分析工具,以下是分享内容:

Java运行时数据区

HotSpot是JDK默认的虚拟机实现。

根据《Java虚拟机规范》,得知Java运行时数据区由下图组成:

幻灯片2

  1. PC(program counter)寄存器:每一条 Java虚拟机线程都有自己的 PC(Program Counter)寄存器,PC 寄存器用于保存 Java 虚拟机正在执行非native方法的的字节码指令的地址。
  2. JVM Stack:java虚拟机栈,每一条 Java 虚拟机线程都有自己私有的 Java 虚拟机栈,java虚拟机栈用于存储栈帧。
  3. Native Method Stack:本地方法栈,用于非java语言的代码执行。
  4. Heap:堆用于存放所有实例对象以及数组对象,这部分内存由JVM通过GC管理内存回收。
  5. Method Area:方法区用于存放类的结构信息,包含运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容等
  6. Runtime Constant Pool:运行时常量池包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用.

 

虚拟机栈

幻灯片3

栈帧随着方法调用而创建,随着方法结束而销毁,栈帧中包含局部变量表,操作数栈,一个指向运行时常量池的引用。

局部变量表包含(boolean、byte、char、short、float、reference,long,double)采用索引的方式定位,发生带参数的方法调用时,创建新栈帧,参数会依次占用局部变量表。

操作数栈为一个LIFO的栈,Java 虚拟机提供一些字节码指令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于从操作数栈取走数据、操作数据和把操作结果重新入栈。在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果。

那么平时工作中能接触到栈帧么?答案是肯定的,如下图,在eclipse中debug的时候可以看见,每一个线程下面随着方法的调用,栈帧随之而创建,方法调用结束而销毁,下图红框部分即为栈帧。

XXX

通过以上介绍,大家应该对Java运行时数据区有个大致的了解了。

HotSpot内存管理

分代管理

有了对java运行时数据区的一个大概了解,我们就可以继续学习一下HotSpot的内存管理方面的知识了。

幻灯片5

现代的虚拟机都将堆分代来管理,那为什么分代呢?分代的依据是什么?分代依据:weak generational hypothesis,即弱代假说:

  1. 大多数对象早死
  2. 老对象很少应用新对象

基于以上假说,HotSpot将堆分为新生代,老年代,而方法区由持久代实现。

新生代

幻灯片6

由于新生代对象98%都是朝生夕死,故采用复制算法回收效率最高,将新生代分为一块Eden,二块Survivor区域。

Eden区域用于新对象的内存分配。Eden内存分配采用bump-the-pointer技术,使用一个指针指向已分配内存的末尾,分配内存时,仅检查剩余内存是否满足新对象分配。效率高。对于多线程内存分配采用Thread-Local Allocation Buffers TLABS,每个线程有自己的一块空闲内存分配缓冲区。不需要任何锁机制,只有当一个TLAB满了以后才需要同步

两块Survivor区域分为From和To,To区域用于下次新生代GC存活对象的存放地,From区域存放着至少活过一次新生代GC的对象。在一次新生代GC结束后,From变为To,To变为From,如下图所示1 2

 

 

老年代

 

幻灯片7

在新生代Survivor区域中活过若干代的对象将晋升至老年代。

大对象直接进入老年代。

在一次minorGC,survivor区域无法容纳时,剩余对象也会进入老年代。而当老年代满时,或者空间分配担保失败时会触发一次majorGC。

垃圾收集

幻灯片8

什么是垃圾?

从GC ROOTS对象出发,不在GC roots引用链上的对象即为垃圾对象

那么哪些对象是GC roots对象呢?

虚拟机栈(栈帧中的本地变量表)中引用的对象。方法区中类静态属性引用的对象。

垃圾收集器

串行收集器

幻灯片9

串行收集器采用串行单线程的方式手机垃圾,新生代采用复制算法,老年代,持久代采用标记-整理算法。

并行收集器

幻灯片10

如上图,Parallel Scavenge收集器是现在jdk1.6,jdk1.7,server模式下默认收集器

-XX:MaxGCPauseMillis最大GC收集时间以及-XX:GCTimeRatio GC收集时间占比。

CMS收集器

幻灯片12

CMS全称Concurrent Mark Sweep,仅能用于老年代,持久代;

CMS如上图所示分为四个阶段:

第一阶段初始标记,仅标记gc root直接关联对象。

第二阶段,并发标记,通过第一阶段的标记对象追踪与GC root关联的对象

第三阶段,重新标记,修正并发期间标记产生变动的对象。

第四阶段,并发清除,清除不可达对象。

在第四阶段并发清除时不能保证清除掉这个阶段产生的垃圾对象,这些垃圾对象称为Floating Garbage。

由于是两个阶段都是并发执行,有可能就会出现一次GC未执行完时,老年代已经没有足够的空间容纳进入老年代的对象了,此种情况称之为Concurrent Mode Failure,当这种情况发生时,GC将stop-the-world直至此次收集结束。

G1

幻灯片13

命令行分析工具

幻灯片14

命令行分析工具应该是每个java程序员必须会用的工具,上图还未列举完整,如Jstack也比较常用。

可视化分析工具

幻灯片15

Jconsole应该大家都是知道的,jvisualVM是一个多合一的分析工具,自带插件体系,不过可用插件不多。MAT是eclipse的一个插件,用于分析dump下来的java堆,可直观的看出是否有内存泄露,对象的信息,对象到GC root的引用链等信息。

案例分析

网站宕机

幻灯片16

去年11月网站宕机大概几个小时,表现为resin假死无响应,通过监控宝看到宕机前活动线程增加至267个后,网站就无响应了。后重启resin恢复,但是在宕机后的几天观察到工作日早上10点,11点resin活动线程均莫名的往上窜,几次也导致网站挂掉。

分析:通过查看nginx日志,发现爬虫请求在第一次宕机前后有30%左右的增加,爬虫一般都爬的商品底层页,商品底层页也是服务器处理比较慢的地方。当时断定为爬虫导致网站宕机。处理办法就是将爬虫限制频率。但是后面也出现了挂掉的情况,后通过在挂掉时把当时的栈dump下来观察才发现是由于首页缓存人工清理掉后,再次加载导致的线程排队等待加载完成的情况导致活动线程数上升,从而导致resin无法创建新线程以响应请求导致的网站无响应,这也能解释之前重启后再次挂掉的情况。也能解释周末不发生此类情况,因为周末首页维护人员一般不会去主动去清除首页缓存。

加盟店商品同步

幻灯片17

加盟店商品同步程序在早上时,服务器的负载经常彪至几十,但是通过重启应用,负载就下来了,原因貌似是应用运行久了以后越跑越慢了。

但是通过heapdump分析得出早上系统全量同步商品时,需要同步的数量太多,而此应用是将这些需要同步的商品每一个都new一个线程放入线程池中执行,负载高是因为此服务器上有7个这样的应用,每个应用开启了20个线程去执行每个应用自己的线程池。重启后,线程池中排队的线程肯定就被丢弃了,所以才有负载降低的现象。如果执行效率低还会造成队列越排越长,从而引发OOM。

此次的分享就到这个地方。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics