解决方案速成

各种OOM(out of memory)办法,速成:

StackOverflowError

out Of memory: PermGen Space:通常是动态类大多,比如web 服务器自动更新部署时引起。-XX:MaxPermSize=256m,一般够用。JDK 8 没有PermGen Space,相对应是MetaSpace

OutOfMemoryError:unable to create native thread

       out Of memory :heap space  : 加大堆的内存:-Xmx 512m, 如果堆的内存一直在增长,那就是没有及时释放对象,主要查下各类集合引用的对象。这类问题最难查,可借且 jvisualvm 程序,仔细分析。

      

原理详解

       下面,开始讲道理

用最简洁的话,解释清楚内存问题。

程序计数寄存器中。

function)。方法有传入的参数,还有传出的参数,执行过程中,还有变量,那么总得给这个方法一块内存,我们叫这块内存为栈(stack),这块内存在方法执行完后,就没必要存在了,就会清理掉,所以你也可以理解成“暂” 时的。

“堆”(heap),可以想象成一个大仓库,总是要堆些东西。在 c 里面用 alloc() 分配,free()还释放, java里用 new 来分配。

本地栈,专门给调用native 程序用的,比如win32的代码。

     

内存释放问题。

      计算机的内存总是有限的,所以,使用完一块内存,最好马上释放掉,给其它程序用。使用很简单,new 一下,一块内存就被用了。但释放就没那么简单了,因为不知道谁该来释放。打个比方,就象超市的塑料袋,厂家生产出来后,给超市,超市又给顾客,那顾客用完后是不是会妥善的把塑料袋处理掉?扔在阴沟里的塑料袋得怎么处理?

      对C/C++程序,必须仔细规划程序结构,及时的调用free()。不过,现实情况就象塑料袋一样,free()函数有时候还真不知道放哪里最合适,即保证需要的人还能用,又要确保没有人用的时候要销毁。

     

自动回收内存机制

       回收的原理就象有辆垃圾车,整天都在城市里转,发现垃圾就收回来。

计数法,如果某个对象 A,被B使用了,计数值就加1,不被使用了就减1。如果计数值是0,那么就回收。这个叫 计数/清理法

标记法。标记法从主程序的对象(root obecjt)开始找,如果某个对象(比如A)被主程序引用,那就是活的,该对象又引用其它的(比如B),那么B也是活的。 如果C对象不被A和B引用,那么C就是死的。i活与死都做个标记,这就叫标记法

清理法

整理法。

复制法。

eden区,第一次复制的时候把eden复制到一个小块的区域,叫 survival 区。再下一次的时候,检查eden区和survival区还活着的对象,再复制到第二个survival区。所以,survial区有两块。

老年代内存,把老年对象复制到这里去。而eden和survival叫新生代区。年老代的内存也会被清理,只是不象新生代区那么频繁,而且不会采用复制法,只采用整理法,因为没多余的内存。

永久代区 permanent generation ,永久代区的信息很难清理,因为不知道某个类是不是会再次被用到。而且最初sun 也只是设置了较小的区域给永久代,因为认为类在程序启动的时候都基本被确定了,不需要很大,但是因为现在的程序大量使用到动态特性,比如web程序中的jsp,就是会被动态编译,各种框架也会用到动态代理机制,所以产生了大量类放到了永久代,不小心就产生了PermGen out of memeory 错误。到了JDK 8,为了解决这个问题,取而代之的是 MetaSpace,其实MetaSpace只是放宽内存的限制,默认是无限。。。

 

       讲到这里,垃圾回收的发现与清理叫完了,接下来得讲怎么执行。也就是垃圾车多久去逛下城市,是只派一辆车吗?还是多派几辆。如果只派一辆,那就是串行执行,如果多派几辆,那么就是并行执行。

       针对 新生代和年老代,是有不同的执行者,采用的策略也有点不一样。

 

垃圾搜集器选择参数

       下面再给JVM 的参数

 

          UseSerialGC:开启此参数使用serial & serial old搜集器(client模式默认值)。

          UseParNewGC:开启此参数使用ParNew & serial old搜集器(不推荐)。

          UseConcMarkSweepGC:开启此参数使用ParNew & CMS(serial old为替补)搜集器。

          UseParallelGC:开启此参数使用parallel scavenge & parallel old搜集器(server模式默认值)。

          UseParallelOldGC:开启此参数在年老代使用parallel old搜集器(该参数在JDK1.5之后已无用)。

 

JVM各个内存区域大小相关参数

          Xms:堆的初始值。默认为物理内存的1/64,最大不超1G。

          Xmx:堆的最大值。默认为物理内存的1/4,最大不超1G。

          Xmn:新生代的大小。

          Xss:线程栈大小。

          PermSize:永久代初始大小。默认为物理内存的1/64,最大不超1G。

          MaxPermSize:永久代最大值。默认为物理内存的1/4,最大不超1G。

          NewRatio:新生代与年老代的比例。比如为3,则新生代占堆的1/4,年老代占3/4。

          SurvivorRatio:新生代中调整eden区与survivor区的比例,默认为8,即eden区为80%的大小,两个survivor分别为10%的大小。(备注:这个参数设定是讲解复制算法那一章中,解决复制算法内存减半的办法。eden区即是复制算法一章中80%的那部分,而survivor区则是两个10%的那部分。)

 

垃圾搜集器性能通用参数

          

          PretenureSizeThreshold:晋升年老代的对象大小。默认为0,比如设为10M,则超过10M的对象将不在eden区分配,而直接进入年老代。

          MaxTenuringThreshold:晋升老年代的最大年龄。默认为15,比如设为10,则对象在10次普通GC后将会被放入年老代。

          DisableExplicitGC:禁用System.gc()。

 

并行搜集器参数

 

          ParallelGCThreads:回收时开启的线程数。默认与CPU个数相等。

          GCTimeRatio:设置系统的吞吐量。比如设为99,则GC时间比为1/1+99=1%,也就是要求吞吐量为99%。若无法满足会缩小新生代大小。

          MaxGCPauseMillis:设置垃圾回收的最大停顿时间。若无法满足设置值,则会优先缩小新生代大小,仍无法满足的话则会牺牲吞吐量。

          

并发搜集器参数

 

          CMSInitiatingOccupancyFraction:触发CMS收集器的内存比例。比如60%的意思就是说,当内存达到60%,就会开始进行CMS并发收集。

          UseCMSCompactAtFullCollection:这个前面已经提过,用于在每一次CMS收集器清理垃圾后送一次内存整理。

 

          CMSFullGCsBeforeCompaction:设置在几次CMS垃圾收集后,触发一次内存整理。