JVM内存分析以及4种内存溢出

一、 HotSpot堆内存结构    

      现在JVM基本上都是HotSpot。接下来先看看堆内存的结构
JVM内存分析以及4种内存溢出
 HotSpot将堆内存分成上面三部分,分别是:新生代(Young Generation)、老年代(Old Generation)、持久代(Permanent Generation)。先大体说下这三部分的作用,然后循序渐进进行深入,学习知识也是要迭代多次,才能更好的理解,一口吃不了一个大胖子,这是题外话了。

新生代(Young Generation)

      新创建的类绝大部分都被分配到这个区域。由于大部分对象创建后很快会不可达到,所以绝大部分对象在新生代创建,然后很快销毁。对象从新生代销毁的过程叫Minor GC。(翻译一下:小型的垃圾回收

      与老年代、持久代不同的是,新生代又被分为三个部分,即上图的Eden,S0,S1。先记住新生代的这三个子部分,下文具体说明。

老年代(Old Generation)

      从新生代对象没有不可达,并且存活下来,会被拷贝到这个区域。这个区域所占的空间比新生代的大,也正是因为这个空间比较大,所以GC的频率比新生代少很多。对象从老年代销毁的过程叫Major GC。(翻译一下:大型的垃圾回收)

持久代(Permanent Generation)

      本人很喜欢这个区域,因为他够持久!男人就要持久。。。

      这个区域也被叫做方法区,存放字节码等文件,常量池也是位于这区域。这个区域如果达到阈值会触发Full GC。Full GC 会清理新生代,老年代以及持久代三个区域。

总结:为什么堆区域会分新生代和老年代?对于大部分应用,常驻对象不多。因为大部分存活寿命不长,新生代和老年代的划分有利于区分对待和缩小垃圾回收范围。(Most allocated objects are not referenced (considered live) for long, that is, they die young. Few references from older to younger objects exist.)

二、HotSpot内存回收策略

      GC(垃圾回收)的一般思路是标记live objects →是否压缩→清理→压缩。

 新生代GC(YouGC)

      Minor GC作用于新生代,从上文可知新生代分为Eden ,S0,S1三个区域,Eden用于生成新的对象,另外两部分是救助区(Survivor Space),这两部分总是转换From、To的角色。先扫描Eden,把存活的对象复制到To,如果To放不下的对象直接拷贝到老年代;再从From区扫描存活的对象,如果达到存活次数的阈值就移动到老年代,剩下的拷贝到To区。这一套完成后From和To的角色互换,维持活动对象在救助区的不断复制,直到它们进入OldGen。(From和To对应上面的S0和S1)

     

老年代GC(OldGC)

      Major GC作用于老年代,相比新生代的YouGC频率很高,Major GC的执行的频率很少。识别过程大致是:识别存活的对象,识别垃圾并回收,然后滑动对象并压缩对象到持续的空间。

总结如下
JVM内存分析以及4种内存溢出
 三、四种内存泄露

  1. 栈溢出(StackOverflowError)
  2. 堆溢出(OutOfMemoryError:java heap space)
  3. 持久代溢出(OutOfMemoryError: PermGen space)
  4. OutOfMemoryError:unable to create native thread

栈溢出StackOverflowError:出现这种错误一般是由于程序问题引起的,比如死递归等。我们知道在函数中基本变量和对象的引用都是在栈内存中分配。当在函数中定义了一个变量时,栈内存会给这个变量分配内存。当超过变量的作用域后,GC将会释放为该变量分配的内存空间。但是如果死递归一直不会结束,并且每一次递归生成的局部变量都不会释放,因为函数还在执行。最终栈不可访问,抛出StackOverflowError.

堆溢出OutOfMemoryError:java heap space: 堆内存溢出会抛出OOME,出现这种问题有可能是内存泄露,也可能是内存溢出。这两种情况比较麻烦,我还在研究阶段。先挖个坑,研究明白了补上。

 持久代溢出(OutOfMemoryError: PermGen space):HotSpot jvm通过持久代实现了java虚拟机规范的方法区,这个区域存放的是class对象、程序运行时的常量池。所以OutOfMemoryError:PermGen space有可能是常量池溢出,也有可能是class对象没有及时释放而引起的溢出。常见的有以下几种情况:

   1.我们平时修改好代码并保存,tomcat会进行热部署。这时候很可能抛出OutOfMemoryError: PermGen space。这是因为原来的class对象没有卸载掉,新的class对象加入方法区,达到方法区的阈值后抛出异常。

    2如果应用程序本身比较大,涉及的类库比较多,但是我们分配给持久带的内存(通过-XX:PermSize和-XX:MaxPermSize来设置)比较小的时候也可能出现此种问题。

   3.class动态生成技术,比如CGLib动态代理等,这种情况下需要更大的方法区空间来存储动态生成的class对象。

OutOfMemoryError:unable to create native thread:字面意思是内存溢出:无法创建新的线程。字面意思已经很明显了,出现这种情况的原因基本下面2点

1.程序创建的线程数超过操作系统的限制。

2.JVM占用的内存太多,导致创建线程的内存空间太小。我们都知道操作系统对每个进程的内存是有限制的,我们启动Jvm,相当于启动了一个进程,假如我们一个进程占用了4G的内存,那么通过下面的公式计算出来的剩余内存就是建立线程栈的时候可以用的内存。 线程栈总可用内存=4G-(-Xmx的值)- (-XX:MaxPermSize的值)- 程序计数器占用的内存 通过上面的公式我们可以看出,-Xmx 和 MaxPermSize的值越大,那么留给线程栈可用的空间就越小,在-Xss参数配置的栈容量不变的情况下,可以创建的线程数也就越小。因此如果是因为这种情况导致的unable to create native thread,那么要么我们增大进程所占用的总内存,或者减少-Xmx或者-Xss来达到创建更多线程的目的。

相关推荐