`

java.lang.OutOfMemoryError引来的一系列之Java内存管理

    博客分类:
  • J2SE
阅读更多
最近网站出现了OutOfMemoryError.
http://japi.iteye.com/blog/261586

对Java的内存管理又深入学习.

过去的学习知道:
Java的内存分为堆与栈,类都在椎上,方法变量都在栈上.
Java中使用垃圾回收机制,减轻了程序员的负担.
Java的垃圾回收机制不等于C++中的析构函数.
Java的垃圾回收机制,程序员只能通过System.gc(),Object.finalize()方法来建议JVM运行垃圾回收.
Java虽然有垃圾回收机制,但也会出现内存溢出.
----------------------------------------------------------------------
以上,学了Java的人都知道.

那还一些不知道的东西.比如:网站报错:PerGen space就不知道是什么东西.

第一步再深入垃圾回收机制
http://www-128.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/index.html
这篇文章讲了基本的原理,以及运用有向图解释了Java与C++的不同.
对于C++程序需要管理节点与边,而Java只需要管理边.这就提高了编程效率.

由此我们要特别加深印象:Java也是会有内存溢出的,只是范围相对C++来说小了点.

第二步再进一步理解垃圾回收机制
知道Java中的对象都存放在堆上,内存有堆与栈的区别是不够的.
必须进一步细分

http://blog.csdn.net/calvinxiu/archive/2007/05/18/1614473.aspx
http://hi.baidu.com/xuwanbest/blog/item/0587d82f2c44a73d1e30892e.html
http://aleung.blogbus.com/logs/4712392.html

以上三篇介绍了垃圾回收机制的回收算法,以及堆的细分
回收算法有三种:
一是复制法:将椎分成两块A,B.从根开始遍历A中的所有活动对象,复制到B中,一次性回收A.
          这样做只需遍历活动对象不需访问死对象,减小了遍历成本,但得付出巨大复制
            代价和较多的内存.
二是标记清除法:先访问所有活动对象并标记,再访问所有内存空间,把没有标记的对象清除.
             遍历成本会随着空间大小线性增加,而且增加碎片.
三是标记整理法:结合以上两种算法的优点.标记活动对象,复制合并成大的内存块.

在以上三种方法之上,又改进了更好的方法:分代
分代成三种:Young , Old, Permanent.


Young:存放生命周期比较短的对象.选用复制算法回收.
    这个区域又分为三个区域:一个Eden,存放新生的对象,两个Survivor(from,to).每次复制就是将Eden和第一块Survior的活对象复制到第2块.对Eden,第一个Survivor进行minor collection(只对Young区回收).
Old:年轻代的对象如果能够挺过数次收集,就会进入老人区.采用复制算法就要反复的复制对象,很不合算,只好采用标记清理算法.
Permanent:
装载Class信息等基础数据,默认64M,如果是类很多很多的服务程序,需要加大其设置-XX:MaxPermSize=,否则它满了之后会引起fullgc()或Out of Memory。 注意Spring,Hibernate这类喜欢AOP动态生成类的框架需要更多的持久代内存。

引用

当一个URL被访问时,内存申请过程如下:
A. JVM会试图为相关Java对象在Eden中初始化一块内存区域
B. 当Eden空间足够时,内存申请结束。否则到下一步
C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区
D. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
E. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)
F. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”


引用


内存溢出的可能性

1. OLD段溢出  java.lang.OutOfMemoryError: Java heap space
这种内存溢出是最常见的情况之一,产生的原因可能是:
1) 设置的内存参数过小(ms/mx, NewSize/MaxNewSize)
2) 程序问题
单个程序持续进行消耗内存的处理,如循环几千次的字符串处理,对字符串处理应建议使用StringBuffer。此时不会报内存溢出错,却会使系统持续垃圾收集,无法处理其它请求,相关问题程序可通过Thread Dump获取(见系统问题诊断一章)单个程序所申请内存过大,有的程序会申请几十乃至几百兆内存,此时JVM也会因无法申请到资源而出现内存溢出,对此首先要找到相关功能,然后交予程序员修改,要找到相关程序,必须在Apache日志中寻找。
当Java对象使用完毕后,其所引用的对象却没有销毁,使得JVM认为他还是活跃的对象而不进行回收,这样累计占用了大量内存而无法释放。由于目前市面上还没有对系统影响小的内存分析工具,故此时只能和程序员一起定位。


2. Perm段溢出   java.lang.OutOfMemoryError: PermGen space
通常由于Perm段装载了大量的Servlet类而导致溢出,目前的解决办法:
1) 将PermSize扩大,一般256M能够满足要求
2) 若别无选择,则只能将servlet的路径加到CLASSPATH中,但一般不建议这么处理

3. C Heap溢出
系统对C Heap没有限制,故C Heap发生问题时,Java进程所占内存会持续增长,直到占用所有可用系统内存.


以上只是讲了些原理...

在实际中,还要知道怎么去优化这些参数...
就得自己多去调试,也可拿别人的数据来验证
引用

服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小



引用
经验之谈:1.Server端JVM最好将-Xms和-Xmx设为相同值。为了优化GC,最好让-Xmn值约等于-Xmx的1/3[2]。2.一个GUI程序最好是每10到20秒间运行一次GC,每次在半秒之内完成[2]。
  • 描述: 三代分布
  • 大小: 11.6 KB
1
0
分享到:
评论
3 楼 east_java 2009-07-21  
引用
Linux:
在/usr/local/apache-tomcat-5.5.23/bin目录下的catalina.sh
添加:JAVA_OPTS='-Xms512m -Xmx1024m'
要加“m”说明是MB,否则就是KB了,在启动tomcat时会报内存不足。
-Xms:初始值
-Xmx:最大值
-Xmn:最小值
Windows
在catalina.bat最前面加入
set JAVA_OPTS=-Xms128m -Xmx350m
如果用startup.bat启动tomcat,OK设置生效.够成功的分配200M内存.
但是如果不是执行startup.bat启动tomcat而是利用windows的系统服务启动tomcat服务,上面的设置就不生效了,
就是说set JAVA_OPTS=-Xms128m -Xmx350m 没起作用.上面分配200M内存就OOM了..
windows服务执行的是bin\tomcat.exe.他读取注册表中的值,而不是catalina.bat的设置.
2 楼 lean1252 2008-11-25  
受益了...
1 楼 east_java 2008-11-05  
引用
从网上的资料看PermSize大一点肯定更好,而且最好是设置PermSize和MaxPermSize一样大。理由如下:
  PermSize 和MaxPermSize如果设置为相同还可以在一定程度上提高性能,因为,PermSize在不断的变化中会需要转移其中的数据。如果固定了以后,则可以减少每次扩大PermSize带来的性能损失

相关推荐

Global site tag (gtag.js) - Google Analytics