- 浏览: 518713 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (161)
- 多线程与并发编程 (20)
- 算法和数据结构 (8)
- 缓存 (0)
- HttpClient (2)
- 疑难杂症 (11)
- Java内存管理 (17)
- 分布式开发 (14)
- Linux常用命令 (10)
- OSGI (0)
- UML (2)
- 趣味面试题 (5)
- 设计模式 (8)
- Java类加载 (2)
- JSTL (1)
- Web 服务器 (4)
- IT人生 (3)
- Java基础 (11)
- Flash技术 (7)
- 新知识 (3)
- 常用速备速查 (4)
- 版本控制 (1)
- Java集合工具类 (6)
- web前端技术 (1)
- 趣味话题 (1)
- 安全 (0)
- 架构设计 (5)
- Spring (4)
- 负载均衡技术 (2)
- 持久层技术 (2)
- MySQL单机多实例方案 (1)
- 收藏备用 (0)
- 性能优化 (3)
最新评论
-
liuwuhen:
...
Pushlet的工作原理 -
fbwfbi:
fengchuizhuming 写道楼主的完全正确。鉴定完毕楼 ...
硬件同步原语(CAS)理论 -
passerby_whu:
uule 写道这个测试后结果为:“testPageConten ...
FutureTask的使用方法和使用实例 -
fengchuizhuming:
楼主的完全正确。鉴定完毕
硬件同步原语(CAS)理论 -
edwardjuice:
FutureTask的使用方法和使用实例
实战 OutOfMemoryError
上述区域中,除了程序计数器,其他在 VM Spec 中都描述了产生 OutOfMemoryError (下称 OOM )的情形,那我们就实战模拟一下,通过几段简单的代码,令对应的区域产生 OOM 异常以便加深认识,同时初步介绍一些与内存相关的虚拟机参数。下文的代码都是基于 Sun Hotspot 虚拟机 1.6 版的实现,对于不同公司的不同版本的虚拟机,参数与程序运行结果可能结果会有所差别。
Java 堆
Java
堆存放的是对象实例,因此只要不断建立对象,并且保证
GC Roots
到对象之间有可达路径即可产生
OOM
异常。测试中限制
Java
堆大小为
20M
,不可扩展,通过参数
-XX:+HeapDumpOnOutOfMemoryError
让虚拟机在出现
OOM
异常的时候
Dump
出内存映像以便分析。(关于
Dump
映像文件分析方面的内容,可参见本文第三章《
JVM
内存管理:深入
JVM
内存异常分析与调优》。)
清单 1 : Java 堆 OOM 测试
/** * VM Args : -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * @author zzm */ public class HeapOOM {
static class OOMObject { }
public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>();
while (true) { list.add(new OOMObject()); } } } |
运行结果:
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid3404.hprof ... Heap dump file created [22045981 bytes in 0.663 secs] |
VM 栈和本地方法栈
Hotspot 虚拟机并不区分 VM 栈和本地方法栈,因此 -Xoss 参数实际上是无效的,栈容量只由 -Xss 参数设定。关于 VM 栈和本地方法栈在 VM Spec 描述了两种异常: StackOverflowError 与 OutOfMemoryError ,当栈空间无法继续分配分配时,到底是内存太小还是栈太大其实某种意义上是对同一件事情的两种描述而已,在笔者的实验中,对于单线程应用尝试下面 3 种方法均无法让虚拟机产生 OOM ,全部尝试结果都是获得 SOF 异常。
1. 使用 -Xss 参数削减栈内存容量。结果:抛出 SOF 异常时的堆栈深度相应缩小。
2. 定义大量的本地变量,增大此方法对应帧的长度。结果:抛出 SOF 异常时的堆栈深度相应缩小。
3. 创建几个定义很多本地变量的复杂对象,打开逃逸分析和标量替换选项,使得 JIT 编译器允许对象拆分后在栈中分配。结果:实际效果同第二点。
清单 2 : VM 栈和本地方法栈 OOM 测试(仅作为第 1 点测试程序)
/** * VM Args : -Xss128k * @author zzm */ public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() { stackLength++; stackLeak(); }
public static void main(String[] args) throws Throwable { JavaVMStackSOF oom = new JavaVMStackSOF(); try { oom.stackLeak(); } catch (Throwable e) { System.out.println("stack length:" + oom.stackLength); throw e; } } } |
运行结果:
stack length:2402 Exception in thread "main" java.lang.StackOverflowError at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:20) at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21) at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21) |
如果在多线程环境下,不断建立线程倒是可以产生 OOM 异常,但是基本上这个异常和 VM 栈空间够不够关系没有直接关系,甚至是给每个线程的 VM 栈分配的内存越多反而越容易产生这个 OOM 异常。
原因其实很好理解,操作系统分配给每个进程的内存是有限制的,譬如 32 位 Windows 限制为 2G , Java 堆和方法区的大小 JVM 有参数可以限制最大值,那剩余的内存为 2G (操作系统限制) -Xmx (最大堆) -MaxPermSize (最大方法区),程序计数器消耗内存很小,可以忽略掉,那虚拟机进程本身耗费的内存不计算的话,剩下的内存就供每一个线程的 VM 栈和本地方法栈瓜分了,那自然每个线程中 VM 栈分配内存越多,就越容易把剩下的内存耗尽。
清单 3 :创建线程导致 OOM 异常
/** * VM Args : -Xss2M (这时候不妨设大些) * @author zzm */ public class JavaVMStackOOM {
private void dontStop() { while (true) { } }
public void stackLeakByThread() { while (true) { Thread thread = new Thread(new Runnable() { @Override public void run() { dontStop(); } }); thread.start(); } }
public static void main(String[] args) throws Throwable { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakByThread(); } } |
特别提示一下,如果读者要运行上面这段代码,记得要存盘当前工作,上述代码执行时有很大令操作系统卡死的风险。
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread |
运行时常量池
要在常量池里添加内容,最简单的就是使用 String.intern() 这个 Native 方法。由于常量池分配在方法区内,我们只需要通过 -XX:PermSize 和 -XX:MaxPermSize 限制方法区大小即可限制常量池容量。实现代码如下:
清单 4 :运行时常量池导致的 OOM 异常
/** * VM Args : -XX:PermSize=10M -XX:MaxPermSize=10M * @author zzm */ public class RuntimeConstantPoolOOM {
public static void main(String[] args) { // 使用 List 保持着常量池引用,压制 Full GC 回收常量池行为 List<String> list = new ArrayList<String>(); // 10M 的 PermSize 在 integer 范围内足够产生 OOM 了 int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } } |
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18) |
方法区
上文讲过,方法区用于存放 Class 相关信息,所以这个区域的测试我们借助 CGLib 直接操作字节码动态生成大量的 Class ,值得注意的是,这里我们这个例子中模拟的场景其实经常会在实际应用中出现:当前很多主流框架,如 Spring 、 Hibernate 对类进行增强时,都会使用到 CGLib 这类字节码技术,当增强的类越多,就需要越大的方法区用于保证动态生成的 Class 可以加载入内存。
清单 5 :借助 CGLib 使得方法区出现 OOM 异常
/** * VM Args : -XX:PermSize=10M -XX:MaxPermSize=10M * @author zzm */ public class JavaMethodAreaOOM {
public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create(); } }
static class OOMObject {
} } |
运行结果:
Caused by: java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632) at java.lang.ClassLoader.defineClass(ClassLoader.java:616) ... 8 more |
本机直接内存
DirectMemory 容量可通过 -XX:MaxDirectMemorySize 指定,不指定的话默认与 Java 堆( -Xmx 指定)一样,下文代码越过了 DirectByteBuffer ,直接通过反射获取 Unsafe 实例进行内存分配( Unsafe 类的 getUnsafe() 方法限制了只有引导类加载器才会返回实例,也就是基本上只有 rt.jar 里面的类的才能使用),因为 DirectByteBuffer 也会抛 OOM 异常,但抛出异常时实际上并没有真正向操作系统申请分配内存,而是通过计算得知无法分配既会抛出,真正申请分配的方法是 unsafe.allocateMemory() 。
/** * VM Args : -Xmx20M -XX:MaxDirectMemorySize=10M * @author zzm */ public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); while (true) { unsafe.allocateMemory(_1MB); } } } |
运行结果:
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at org.fenixsoft.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:20)
发表评论
-
JVM四种对象引用类型
2011-05-17 16:27 1553JVM 中将对象的引用分为了四种类型,不同的对象引用类型会造成 ... -
Garbage First - Card Table
2011-05-12 17:36 1451之前一直以为只是在Garbage First垃圾回收器中,有C ... -
Red5 0.7和0.9 版本配置远程JConsole监控
2010-12-12 16:23 2515JConsole可以帮助你监控虚拟机的状态,垃圾回收的时间,调 ... -
Java内存Dump文件查看和分析工具介绍
2010-12-06 16:51 15539为了查red5-0.7.0的内存泄露问题,由于dump文件太大 ... -
深入垃圾收集器与内存分配策略
2010-11-05 15:45 1373Java与C++之间有一堵由 ... -
JVM 运行时区域划分(转载)
2010-11-05 15:39 1571VM 运行时数据区域 JVM 执行 Java ... -
使用jconsole远程监控linux下tomcat使用情况
2010-10-26 09:11 3671启动tomcat/bin/startup.sh中增加一下变量: ... -
Eclipse经常出现OutOfMemory错误,如何解决
2010-04-15 16:59 23049今天有个同事问我一个问题,他的Eclipse经常出现OutOf ... -
Java内存泄露示例代码总汇
2010-04-09 13:57 3563Java尽管采用自动的内存 ... -
HotSpot VM - OutOfMemory Error 总汇
2010-04-06 16:02 2208OutOfMemory Error表明Java 堆没有足够的内 ... -
Java内存管理-Permanent Space
2010-03-10 13:21 4989VM 的Permanent generation space, ... -
JVM -Server -Client 模式
2010-03-10 09:39 2754JVM -Server 与 -Client启动,最主要的差别在 ... -
Java内存管理-监控工具篇
2010-03-09 22:31 120631.JConsole Jconsole,Java ... -
Java 5 版本以上(SUN JVM)GC参数化调整大全
2010-03-08 16:37 2820今天终于有所斩获,发现了Sun Hotspot JVM GC最 ... -
Java内存管理基础篇 - Java内存回收-常见的垃圾回收策略
2010-03-08 16:33 1719Java的内存使用完之后,是通过JVM的GC进行回收的。 ... -
Java内存管理基础篇 - Java内存分配
2010-03-04 22:19 29661.简介 现摘录一段Java5内存管理白皮书中的 ...
相关推荐
java.lang.OutOfMemoryError: Java heap space 解决方法
ant编译时抛出OutOfMemoryError.doc
遭遇OutOfMemoryError 的解决方法。
java.lang.OutOfMemoryError处理错误
java.lang.OutOfMemoryError,产生该错误的原因大都出于以下原因: JVM内存过小、程序不严密,产生了过多的垃圾.
java.lang.OutOfMemoryError: PermGen space 解决方案
解决OutOfMemoryError: PermGen space,过程是痛苦的,结果是舒畅的
tomcat 出现 OutOfMemoryError 的解决方法
报错 java.lang.OutOfMemoryError: PermGen space 报错 java.lang.OutOfMemoryError: Java heap 启动报错java.lang.ClassNotFoundException: 1catalina.org.apache.juli.FileHandler JAVA_OPTS="-server -Xms800m ...
Myeclipse下java.lang.OutOfMemoryError Java heap space的解决
搜集整理关于java错误处理:java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: Java heap space 资料整理
java.lang.OutOfMemoryError: PermGen space
OutOfMemoryError_8种典型案例分享,定位java内存问题
java解决nested exception is java.lang.OutOfMemoryError Java heap space 解决OOM
java.lang.OutOfMemoryError: Java heap space 解决方法
内存不足OOM java.lang.OutOfMemoryError.
Caused by: java.lang.OutOfMemoryError: PermGen space解决方案, avax.servlet.ServletException: java.lang.OutOfMemoryError: PermGen space at org.apache.jasper.servlet.JspServlet.service(JspServlet.java...
编译时出现java.lang.OutOfMemoryError Java heap space异常.
java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法java[1].lang.OutOfMemoryError_Java_heap_space错误及处理办法