
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List<byte[]> buffer = new ArrayList<byte[]>();
buffer.add(new byte[10*1024*1024]);
}
}
我们通过以下命令运行以上代码:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest
程序输出以下信息:
[GC 1180K->366K(19456K), 0.0037311 secs]
[Full GC 366K->330K(19456K), 0.0098740 secs]
[Full GC 330K->292K(19456K), 0.0090244 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at OOMTest.main(OOMTest.java:7)

从运行结果可以看出,JVM执行了两次Minor gc和一次Major gc. 从Major gc的输出中可以看出,gc之后的旧区域的使用率为134K,字节数组为10M,加起来大于旧一代的空间,因此引发异常. 如果您调整-Xms21M,-Xmx21M,则不会触发gc操作,并且不会有异常.
上面的实验实际上从侧面验证了一个结论: 当对象大于新一代的剩余内存时,它将直接放置在旧一代中. 当仍然无法放下旧一代的剩余内存时,将触发垃圾回收. 如果您不能放下它,则会引发内存溢出异常.
永久性频带溢出(OutOfMemoryError: PermGen空间)
我们知道Hotspot jvm通过持久性磁带在Java虚拟机规范中实现了方法区域,并且运行时常量池存储在方法区域中,因此持久性磁带溢出可能是运行时常量池溢出,也可能是be方法区域中存储的类对象没有及时回收,或者类信息占用的内存超出了我们的配置.
当永久性磁带溢出时,抛出java.lang.OutOfMemoryError: PermGen空间. 它可能出现在以下情况下:

使用某些应用程序服务器的热部署时,经过几次热部署后,我们将遇到内存溢出. 这是因为在每次热部署后都不会卸载原始类. 如果应用程序本身相对较大并且包含更多的类库,但是当内存相对较小时,也可能会出现分配给持久性磁带的内存(由-XX: PermSize和-XX: MaxPermSize设置). 一些第三方框架(例如spring和hibernate)通过字节码生成技术(例如CGLib)实现了一些增强的功能. 在这种情况下,可能需要更大的方法区域来存储动态生成的Class文件.
我们知道Java中的字符串常量位于常量池中. 当String.intern()方法运行时,它将检查常量池中是否存在与此字符串相等的对象,如果存在与该字符串相等的对象,则将直接返回常量. 的对象在池中不存在,请首先将字符串添加到常量池中,然后返回该字符串的引用. 然后,我们可以使用String.intern方法在运行时模拟常量区域的溢出. 下面,我们使用以下代码来模拟这种情况:
import java.util.*;
import java.lang.*;
public class OOMTest{
public static void main(String... args){
List list = new ArrayList();
while(true){
list.add(UUID.randomUUID().toString().intern());
}
}
}
我们通过以下命令运行以上代码:
java -verbose:gc -Xmn5M -Xms10M -Xmx10M -XX:MaxPermSize=1M -XX:+PrintGC OOMTest

运行后的输入如下图所示:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at OOMTest.main(OOMTest.java:8)
通过以上代码,我们在运行时成功模拟了常量池的溢出. 从输出中的PermGen空间可以看出,持久性区域确实溢出了. 这也验证了我们前面提到的Hotspot jvm通过了持久化带来的实现方法领域.
OutOfMemoryError: 无法创建本机线程
最后,让我们看一下错误java.lang.OutOfMemoryError: 无法创建natvie线程. 发生这种情况时,通常是由以下两种情况引起的:

程序创建的线程数超过了操作系统的限制. 对于Linux系统,我们可以使用ulimit -u查看此限制. 分配给虚拟机的内存太大,导致创建线程时所需的本地内存太少.
我们都知道操作系统对每个进程的内存都有限制. 我们启动Jvm,这等效于启动一个进程. 如果一个进程占用4G内存,则通过以下公式计算的剩余内存是创建线程堆栈时可以使用的内存. 线程堆栈的可用总内存= 4G-(-Xmx的值)-(-XX的值: MaxPermSize)-程序计数器占用的内存
从上面的公式中,我们可以看到-Xmx和MaxPermSize的值越大,线程堆栈可用的空间越小. 在由-Xss参数配置的堆栈容量保持不变的情况下,可以创建它. 线程数也较小. 因此,如果由于这种情况无法创建本机线程,则我们要么增加进程占用的总内存,要么减少-Xmx或-Xss来达到创建更多线程的目的.
(本文结尾)
参考:
-
-
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-297350-1.html
最近更新的都怕了