通过简单的小例子程序,演示java虚拟机各部分内存溢出情况: (1).java堆溢出: Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常。 想要方便快速地产生堆溢出,要使用如下java虚拟机参数:-Xms10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10MB,最小堆内存和最大堆内存相同是为了避免堆动态扩展),-XX:+HeapDumpOnOutOfMemoryError可以让java虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析。 例子代码如下: public class HeapOOM{ static class OOMObject{ } public static void main(String[] args){ List<OOMObject> list = new ArrayList<OOMObject>(); while(true){ list.add(new OOMObject()); } } } public class HeapOOM{ static class OOMObject{ public static void main(String[] args){ List<OOMObject> list = new ArrayList<OOMObject>(); while(true){ list.add(new OOMObject()); } } } 运行一段时间就会发现产生OutOfMemoryError异常,并且产生了堆内存异常dump文件。 (2).java虚拟机栈和本地方法栈溢出: 由于Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,因此对于HotSpot虚拟机来说-Xoss参数(设置本地方法栈大小)虽然存在,但是实际上是无效的,栈容量只能由-Xss参数设定。 由于Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,所以分别使用两个例子演示这两种情况: a.java虚拟机栈深度溢出: 单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码如下: public class JavaVMStackOF{ private int stackLength = 1; public void stackLeak(){ statckLength++; stackLeak(); } public static void main(String[] args){ JavaVMStackOF oom = new JavaVMStackOF(); oom.stackLeak(); } } public class JavaVMStackOF{ private int stackLength = 1; public void stackLeak(){ statckLength++; stackLeak(); } public static void main(String[] args){ JavaVMStackOF oom = new JavaVMStackOF(); oom.stackLeak(); } } 运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出一般会产生在方法递归调用过多而java虚拟机栈内存不够的情况下。 b.java虚拟机栈内存溢出: 多线程环境下,能够创建的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存一定的情况下,单个线程占用的内存越大,所能创建的线程数目越小,所以在多线程条件下很容易产生java虚拟机栈内存溢出的异常。 使用-Xss2m参数设置java虚拟机栈内存大小为2MB,例子代码如下: public class JavaVMStackOOM{ private void dontStop(){ while(true){ } } public void stackLeakByThread(){ while(true){ Thread t = new Thread(new Runnable(){ public void run(){ dontStop(); } }); t.start(); } } public static void main(String[] args){ JavaVMStackOOM oom = new JavaVMStackOOM(); oom. stackLeakByThread();. } } public class JavaVMStackOOM{ private void dontStop(){ while(true){ } } public void stackLeakByThread(){ while(true){ Thread t = new Thread(new Runnable(){ public void run(){ dontStop(); }}); t.start(); }} public static void main(String[] args){ JavaVMStackOOM oom = new JavaVMStackOOM(); oom. stackLeakByThread(); }} 运行一段时间之后,java虚拟机栈就会因为内存太小无法创建线程而产生OutOfMemoryError。 (3).运行时常量池溢出: 运行时常量池属于方法区的一部分,可以使用-XXPermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。 String的intern()方法用于检查常量池中如果有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,否则,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。因此String的intern()方法特别适合演示运行时常量池溢出,例子代码如下: public class RuntimeConstantPoolOOM{ public static void main(String[] args){ List<String> list = new ArrayList<String>(); list.add(String.valueOf(i++).intern()); } } } public class RuntimeConstantPoolOOM{ public static void main(String[] args){ List<String> list = new ArrayList<String>(); int i = 0; while(true){ list.add(String.valueOf(i++).intern()); } } } 运行一段时间,永久代内存不够,运行时常量池因无法再添加常量而产生OutOfMemoryError。 (4).方法区溢出: 运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法区用于存放Class的相关信息,Java的反射和动态代理可以动态产生Class,另外第三方的CGLIB可以直接操作字节码,也可以动态产生Class,实验通过CGLIB来演示,同样使用-XXPermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。例子代码如下: 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(); } } class OOMObject{ } } 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(); }} class OOMObject{} } 运行一段时间之后,永久代内存不够,方法区无法再存放CGLIB创建处理的Class信息,产生方法区OutOfMemoryError。 (5).本机直接内存溢出: Java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,如果不指定,则默认与java堆内存大小相同。JDK中可以通过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操作本机直接内存。通过使用-XX:MaxDirectMemorySize=10M,限制最大可使用的本机直接内存大小为10MB,例子代码如下: public class DirectMemoryOOM{ private static final int _1MB = 1024* 1024 * 1024; publc 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直接想操作系统申请内存 unsafe.allocateMemory(_1MB); } } } public class DirectMemoryOOM{ private static final int _1MB = 1024* 1024 * 1024; publc 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直接想操作系统申请内存 unsafe.allocateMemory(_1MB); } } } 当运行一段时间之后,10MB的本机直接内存被分配光,无法在进行直接内存分配时,产生OutOfMemoryError。 本文由守望者watchmen收集整理,部分内容源于网络(http://it.deepinmind.com)。本文仅代表作者个人观点,不代表守望者的本意。如有违法侵权内容,请提交到守望者管理员处,立即处理。 |
行业聚焦 面试交流 职位推荐 开发视频 技术交流 腾讯微博 新浪微博
友情链接:课课家教育 阿里云 鲜果 W3Cfuns前端网 中国企业家 环球企业家 投资界 传媒梦工场 MSN中文网 Android开发者社区 cnbeta 投资中国网 又拍云存储 美通说传播 IT茶馆 网商在线 商业评论网 TechOrange IT时代周刊 3W创新传媒 开源中国社区 二维工坊 Iconfans 推酷 智能电视网 FreeBuf黑客与极客 财经网 DoNews 凤凰财经 新财富 eoe移动开发者社区 i黑马 网易科技 新浪科技 搜狐IT 创业家 创业邦 腾讯财经 福布斯中文网 天下网商 TechWeb 雷锋网 新浪创业 和讯科技 品途O2O 极客公园 艾瑞网 抽屉新热榜 卖家网 人民网通信频道 拉勾网 创新派 简单云主机
手机版|黑名单|守望者 成才网 在线教育 linux 高级程序设计 C/C++ 大数据
( 蜀ICP备14029946号 )
成都守望者科技有限公司 © 2013-2016 All Rights Reserved