JVM之垃圾回收遇到的问题

JVM之垃圾回收遇到的问题

 

怎么判断对象可以被回收

 

根搜索算法

 

 

在主流的商用程序语言中(java和c#),都是使用根搜索算法(GC Roots Tracing)判断对象是否存活的。这个算法的基本思路就是通过一系列的名为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。

 

java中可以作为GC Root的对象

 

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

 

 

如何处理内存碎片

  1. 将标记-清除算法改为复制、标记-整理算法
  2. 在标记-清除算法执行完后,执行一次内存整理

 

 

如何解决同时存在的对象创建和对象回收问题

 

垃圾回收线程是回收内存的,而程序运行线程则是消耗(或分配)内存的,一个回收内存,一个分配内存,从这点看,两者是矛盾的。

 

 

解决方法一

 

在现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(即:暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用。这种实现方式是最直接,而且最有效的解决二者矛盾的方式。

 

缺点:当堆空间持续增大时,垃圾回收的时间也将会相应的持续增大,对应应用暂停的时间也会相应的增大。一些对相应时间要求很高的应用,比如最大暂停时间要求是几百毫秒,那么当堆空间大于几个G时,就很有可能超过这个限制。

 

 

解决方法二

 

使用并发垃圾回收算法,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题。

 

缺点:因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。

 

系统崩溃前的一些现象:

  • 每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5s
  • FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
  • 年老代的内存越来越大并且每次FullGC后年老代没有内存被释放
  • 之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值。

怎么知道服务器访问越来越慢的原因呢?

  • 来几次内存快照信息,分析快照信息
  • 看是不是内存溢出,导致垃圾回收时间变长,垃圾回收时间变长,就会影响程序运行
  • 生成当前的Heap dump信息,大小为一个3G(整个堆的大小)的hprof文件
  • 服务器访问慢,可能就是系统快要崩溃了,系统崩溃前垃圾回收特别慢,就会造成服务器反应慢啊!!!

怎么触发 Java heap dump(生成内存快照)

  • 使用$JAVA_HOME/bin/jmap -dump来触发,eg:jmap -dump:format=b,file=/home/longhao/heamdump.out
  • 使用$JAVA_HOME/bin/jcosole中的MBean,到MBean>com.sun.management>HotSpotDiagnostic>操作>dumpHeap中,点击 dumpHeap按钮。生成的dump文件在java应用的根目录下面。
  • 在应用启动时配置相关的参数 -XX:+HeapDumpOnOutOfMemoryError,当应用抛出OutOfMemoryError时生成dump文件。
  • 使用hprof。启动虚拟机加入-Xrunhprof:head=site,会生成java.hprof.txt文件。该配置会导致jvm运行非常的慢,不适合生产环境。

为什么崩溃前垃圾回收的时间越来越长

根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

为什么Full GC的次数越来越多

因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

为什么年老代占用的内存越来越大

因为年轻代的内存无法被回收,越来越多地被Copy到年老代

猜你喜欢

转载自youyu4.iteye.com/blog/2354079