OutOfMemoryError–Java heap space
问题
代码中试图向JVM申请内存空间,但是没有足够的空间。
注意:机器的物理空间足够,但是JVM的堆大小限制,也会导致出错。
原因分析
- 最直接的原因就是你配置的堆内存太小,或者你的机器内存不够。
- 用户量请求量或者书籍处理量上来后,程序所需的资源,远远大于平常,导致JVM堆内存不够用。
- 内存泄漏,不知情的情况下,内存被某些功能中的对象锁占用了。通常是编码过程中的错误方式导致对象使用后无法被回收。
/**
*堆大小不够导致的 OutOfMemoryError(Java heap space)
* 启动参数 -Xmx512m -server
*/
public class OomDemo01 {
private final static int size=1024*1024*43;
public static void main(String[] args) {
long [] arr=new long[size];
}
}
新生代:老年代=1:2,上面代码中设置堆内存是512m,老年代大小是341左右,long类型占8 byte。
运行结果:
private final static int size=1024*1024*42;
改成这个就不会报错了。
解决思路
- 配置更大的堆内存: -Xmx
java -Xmx1073741824 com.tony.edu.Demo
java -Xmx1048576k com.tony.edu.Demo
java -Xmx1024m com.tony.edu.Demo
java -Xmx1g com.tony.edu.Demo
实际情况比较复杂,比如内存泄漏,你需要利用JVM调试工具,监控工具来分析堆内存中具体的情况,例如:jmap,jvisualvm,mat等工具。
OutOfMemoryError–GC overhead limit exceeded
问题
当GC花费了程序运行总时间的98%以上,而回收不到2%的堆,则抛出该异常。
The parallel collector throws an OutOfMemoryError if too much time is being spent in garbage collection (GC): If more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, then an OutOfMemoryError is thrown. This feature is designed to prevent applications from running for an extended period of time while making little or no progress because the heap is too small. If necessary, this feature can be disabled by adding the option -XX:-UseGCOverheadLimit to the command line.
原因分析
程序运行时内存不够用,触发GC任务执行。GC工作会导致Stop The World 的事件发生,STW意味这个GC过程中所有的应用程序线程都将停止一定的时间,意味这你的业务代码执行被停顿。
/**
* 堆内存大小设置为:-Xmx8m
*/
public class OomDemo02 {
public static void main(String[] args)throws Exception {
Map map =System.getProperties();
Random r=new Random();
try {
while (true){
map.put(r.nextInt(),"value");
}
}catch (Throwable e){
e.printStackTrace();
}
}
}
解决思路
这个异常不是很容易重现。因为GC时也意味着堆内存不够,可能实际抛出的是更常见的java.lang.OutOfMemoryError: Java heap space,比如你把上面这个例子堆内存设置为16m或者更大的时候。
也不要尝试通过 -XX:-UseGCOverheadLimit 参数关闭这个功能,否则导致无法看到
java.lang.OutOfMemoryError完整的信息。
OutOfMemoryError-- Metaspace或者 Permgen space
java8之前是java.lang.OutOfMemoryError: PermGen space错误 ,java8(包含java8)之后是 java.lang.OutOfMemoryError: Metaspace。
问题
方法区内存不足:永久代/元数据空间用了存放类的名称和字段,带有方法字节码的方法,常量池信息,与类和关联的对象数据和类型数据,JIT即时编译优化。
原因分析
Metaspace 的大小需求即依赖于装入类的数量,也依赖于此类的声明的大小。因此,我们可以说这个异常主要原因是太多类或太大类被加载到元数据空间。
代码:
import javassist.CannotCompileException;
import javassist.ClassPool;
public class OomDemo03 {
public static void main(String[] args) throws CannotCompileException {
for (int i = 0; i <100000000 ; i++) {
Class gengate = gengate("cn.tkxb.omm" + i);
}
}
public static Class gengate(String name) throws CannotCompileException {
ClassPool aDefault = ClassPool.getDefault();
return aDefault.makeClass(name).toClass();
}
}
运行结果:
注意,运行这段代码时,可能看到的不是java.lang.OutOfMemoryError: Metaspace,javassist 内部捕获了 java.lang.OutOfMemoryError: Metaspace,
表面上看起来的错误可能会是:java.lang.ClassFormatError: Metaspace
WEB服务器运行期间多次部署项目
例如Tomcat服务器反复部署项目,如果代码编写有问题,导致类加载器及类没有卸载,就可能导致方 法区相关的溢出
解决思路
java 8
-XX:MetaspaceSize=16m -XX:MaxMetaspaceSize=16m
java 8之前
-XX:MaxPermSize=512m
-XX:MaxPermSize=512m
运行过程中:保存异常时的内存快照,通过Eclipse MAT等工具进行事后分析。
在分析器中,可以查找重复的类,特别是那些加载应用程序类的类。继而查找到找到当前活动的类加载 器。
对于非活动的类加载器,通过从非活动类加载器获取到GCroot的最短路径来确定具体是哪段代码,甚至 定位到哪一个第三方包
OutOfMemoryError–5.4Unable to create new native thread
问题
java应用程序已经达到它可以启动的线程的限制。
原因分析
JVM请求操作系统中创建新线程时,当底层操作系统不能分配一个新的本地线程,这个OutOfMemoryError将抛出。
- 在JVM中运行的应用程序请求一个新的线程
- JVM本机代码将请求代理为操作系统创建一个新的本机线程。
- 操作系统试图创建一个新地本机线程,它需要为线程分配内存
- 操作系统将拒绝本机内存分配,因为32位进程大小耗尽了他的内存地址空间或者操作系统的虚拟内存已经安全耗尽
- 最终抛出异常
不同的平台有不同的表现
public class OomDemo04 {
public static void main(String[] args) {
while (true){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
}
解决思路
linux服务器上通过命令查看,可以通过修改配置加大这个阀值。
这问题,大部分情况都不是系统配置问题,通常意味着你的程序有着重大的错误
OutOfMemoryError–Out of swap space?
问题描述
JVM请求的总内存大于可用物理内存的情况下,操作系统开始将内存从内存交换到硬盘驱动器,物理内存和交换空间的缺乏,分配失败
原因分析
一般来说,JVM有自己的GC机制,如果出现这个异常,通常意味着是下面的原因。操作系统配置的交换空间不足。系统上的另一个进程正在消耗所有内存资源。
解决思路
最简单的解决方法时增加交换空间。
例如在Linux中,使用以下命令来实现,创建并附加一个大小为640MB的新swapfile
一般来说,这个异常意味着你要升级内存或者意味这个你的一台机器不够用了,用户请求过多或者出了的数据量很大,需要集群。
OutOfMemoryError–Requested array size exceeds VM limit
问题描述
搞了一个大数组,分配一个比java虚拟机所支持的更大的数组。
原因分析
错误是由JVM中的本机代码抛出的,当JVM执行特定于平台的检查时,它发生在数组分配内存之前,分配的数据结构在这个平台是否可以寻址。
public class OomDemo05 {
public static void main(String[] args) {
byte[]bytes=new byte[Integer.MAX_VALUE];
}
}
解决思路
代码问题,不要搞这么长的数组。
OutOfMemoryError–Direct buffer memory
堆外内存溢出
OutOfMemoryError–Kill process or sacrifice child
问题
操作系统的保护机制,内存不够是,干掉内存占用最大的进程,看操作系统日志有提示
原因分析
Linux 是以进程为单位管理操作系统相关资源的,Linux中有一个称为OUt of memory killer内存工作线程,一般情况下,当操作系统检测到低内存情况时,将激活Out of memory killer杀死内存占用非常大的用户进程。
解决思路
既然是JAVA程序,那么尽量控制堆的大小,让它不要对整个服务器造成很大的影响,所有我们通常会限制最大堆不超过整个操作系统资源的80%。
系统kill你的JVM进程,一般会留下痕迹(系统日志 /var/log/message)关键字 kill