写在前面:这里立此随笔的主要目的是为了记录本人在开发工程中遇到的内存溢出的情况,希望借此能够内省自己,留下经验。同时正好可以在这里详细回顾一下java的内存分配和jvm工作原理,总结出自己的一些理解。博客系本人在开发中遇到的问题通过查阅后的总结,如有错误,望指导和建议,谢谢!
1.Java的内存分配
Java代码最终将会在JVM上执行,而JVM也会有自己的内存空间的分配,用来完成不同不同的工作。
Java内存分为若干区域:程序计数器、Java虚拟机栈(存储临时变量表,操作数栈,动态链接,方法入口),对应着栈的出栈入栈过程,本地方法栈(调用本地方法),Java堆(内存最大的一块,所有线程共享的区域,在虚拟机启动时创建,此内存区的唯一目的是存放对象实例,包括数组,Java堆是垃圾管理器管理的的主要区域,分为新生代,老年代,永生代)、方法区(跟Java堆一样,也是所有线程共享的区域,用来加载类信息,常量、静态变量、及时编译的代码数据,也是垃圾搜集器同一管理)、运行时常量池(用于存放编译期生成的各种字面常量和符号引用,Java并不要求所有常量在编译期才产生,例如字符串); 程序计数器、虚拟机栈、本地方法栈区域是随着线程而生而亡,不需要过多考虑内存回收。而Java堆和方法区确定算法:
- 引用计数算法:python FlashPlayer、Squirrel:存在循环引用
- 可达性分析算法(GC roots)是否到达
- 垃圾搜集算法:标记清除算法,标记整理算法、复制算法
2.Java虚拟机内存异常:
2.1 java.lang.OutOfMemoryError: Java heap space
2.1.1 堆溢出情景一
今天在使用eclipse集成环境编写代码的时候,执行时系统抛出异常:java.lang.OutOfMemoryError: Java heap space 。初次定位异常的位置是在一段测试代码的for循环中:
int i = xxxx;
List<FileBean> list = new ArrayList<FileBean>(); List<FileBean> fileListA = new ArrayList<FileBean>(); List<FileBean> fileListB = new ArrayList<FileBean>();for (int j = 0; j < 100; i++) { FileBean fileBeanA = new FileBean(); fileBeanA.setFilePath("E:\1000测试合并\A文件测试word合并.docx"); fileBeanA.setNewFileName("A文件测试word合并"); fileListA.add(fileBeanA);
FileBean fileBeanB = new FileBean(); fileBeanB.setFilePath("E:\1000测试合并\B文件测试word合并.docx"); fileBeanB.setNewFileName("B文件测试word合并"); fileListB.add(fileBeanB);
}
刚开始并没有发现这段代码有什么问题,甚至还以为是GC的时候出现bug了。
因此对于这个问题,还做了关于类的加载方式和GC回收的机制的温习和确认。。。
结果发现是个很尴尬的问题 for 循环的递增条件是 i++,而非 j++,也就是说死循环...怎么能不溢出呢?
抛出的异常也是堆溢出,我们都知道对象创建后就是放到堆空间上,因此也解释了 java.lang.OutOfMemoryError: Java heap space 异常现象。
粗心开始开发过程中的一个大忌,一定要细心。
---------------------------分割线-----------------------------
2.2 java.lang.StackOverflowError
待续...