Java 虚拟机规范的描述中,除了程序计数器外,虚拟机内存的几个运行时区域都可能发生OutOfMemoryError(OOM)。
Java 堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制清除这些对象,那么在对象数量达到最大堆的容量限制后就会产生内存溢出。
要解决这种瓿,一般是先通过内存分析工具对Dump出来的堆转储快照文件进行分析,确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄露,可进一步通过工具查看泄露对象到GC Roots的引用链。于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。掌握了泄露对象的类型信息及GC Roots引用链的信息,就可以比较准确的定位出泄露代码的位置。
如果不存在泄露,就是内存中的对象确实还必须存活,那就应当检查虚拟机的堆参数(-Xms和-Xmx)与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过
长,持有状态时间过长的情况,尝试减少程序运行期的内存消耗。
HotSpot虚拟机中并不区分虚拟机栈和本地方法栈。因此,-Xoss参数无效,-Xss参数可以设定栈容量。Java虚拟机规范中描述了栈相关的两种异常:
- 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
- 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
在单线程中,尝试定义大量的本地变量、大量的方法参数均未产生OutOfMemoryError异常,运行结果都是StackOverflowError异常。代码如下:
运行结果:
结论:在单线程中,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。
如果不限于单线程,通过不断地建立线程的方式可以产生内存溢出异常。但是这样产生内存溢出与栈空间是否足够大并不存在任何联系,或者准确地说,在这种情况下,为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。
运行结果:
Windows平台中,该代码执行时可能会导致操作系统假死。如果尝试运行该代码,请务必先保存好当前的工作。
String.intern()是一个native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。
在JDK1.6及之前版本,常量池位于永久代中,可以通过-XX:PermSize 和 -XX:MaxPermSize限制方法区大小,从而间接限制常量池的容量。
在JDK8中,字符串常量池位于堆中,上述参数无效了,可以通过-Xmx指定最大堆空间。
运行结果:
关于常量池,这里有一个有意思的现象:
这段代码在不同版本的JDK运行结果可能有差异:
- JDK 1.6:两个false
- JDK8:true和false
产生上述差异的原因是:
- JDK1.6中,intern会把首次遇到的字符串实例复制到永久代中,并返回永久代中这个字符串的引用。
- JDK8中,调用intern时,如果池已经包含与equals(Object)方法确定的相当于此String对象的字符串,则返回来自池的字符串。 否则,此String对象将添加到池中,并返回对此String对象的引用。
DirectMemory容量可以通过-XX:MaxDirectMemorySize指定,默认是Java堆最大值。
运行结果:
由DirectMemory导致的内存溢出,明显特征是:在Heap Dump文件中不会看见明显的异常。
到此这篇bytebuffer.allocate内存溢出(bufferedreader.readline()内存溢出)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/47816.html