内存溢出
内存溢出,Out Of Memory,简称OOM。程序无法获得足够的内存空间时,就会出现内存溢出。内存溢出会导致程序无法正常运行或者自动关闭,可能在重启服务后又可以正常运行(释放掉了部分内存),但是该问题仍会存在。
内存溢出的具体方式
1、栈溢出
相对于堆来说,栈的内存比较小,这也就意味着栈中存放的数据是有限的,当需要存放的数据大于栈的内存时,出现栈溢出。
1)、递归(Test.calss,设置成-Xss64K):
package com.su.mybatis.oracle.controller;
public class Test {
private static int num = 1;
public static void main(String[] args) {
try {
new Test().add();
System.out.println("----over----");
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("num :" + num);
}
}
private void add() {
num ++;
if(num < 1000) {//模拟需要递归调用1000次场景
add();
}
}
}
输出结果:
num :800
Exception in thread "main" java.lang.StackOverflowError
at com.su.mybatis.oracle.controller.Test.add(Test.java:20)
at com.su.mybatis.oracle.controller.Test.add(Test.java:22)
设置-Xss128k
输出结果:
----over----
num :1000
2)、创建线程(设置-Xss10M)
package com.su.mybatis.oracle.controller;
public class Test {
public static void main(String[] args) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + "开始...");
for(int i = 0; i < 1000; i++) {
new Thread(){
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"开始...");
try {
Thread.sleep(10);
} catch (Exception e){
e.printStackTrace();
} finally {
System.out.println("线程"+Thread.currentThread().getName()+"结束...");
}
}
}.start();
}
Thread.sleep(500);//保证如果线程下面打印在最后
System.out.println("----over----");
}
}
输出结果:
...
线程Thread-261开始...
线程Thread-262开始...
线程Thread-244结束...Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:713)
at com.su.mybatis.oracle.controller.Test.main(Test.java:20)
线程Thread-265开始...
线程Thread-245结束...
...
设置-Xss32k
输出结果:
线程Thread-997结束...
线程Thread-996结束...
线程Thread-999结束...
----over----
2、堆溢出
Java堆上会分配所有对象的实例的内存。如果java堆上已经没有内存,但是仍然有新的对象实例生成,或者申请空间过大,出现堆溢出。
1)、while(true)循环(设置-Xms2M -Xmx2M):
package com.su.mybatis.oracle.controller;
import java.util.LinkedList;
import java.util.List;
public class Test {
private static int num = 1;
public static void main(String[] args) {
try {
List<String> list = new LinkedList<>();//ArrayList会自动扩容,扩容的实现是list旧数据赋值到新list中,影响性能
while(true) {
num++;
if(num > 100000) {
return;
}
list.add(new String());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("num:" + num);
}
}
}
输出结果:
num:88570
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.LinkedList.linkLast(LinkedList.java:140)
at java.util.LinkedList.add(LinkedList.java:336)
at com.su.mybatis.oracle.controller.Test.main(Test.java:18)
设置-Xms10M -Xmx10M
输出结果:
num:100001
2)、申请空间过大,直接抛出堆溢出
package com.su.mybatis.oracle.controller;
public class Test {
public static void main(String[] args) {
int[] i = new int[100000000];
System.out.println(i[0]);
}
}
输出结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.su.mybatis.oracle.controller.Test.main(Test.java:5)
3、方法区溢出
不停的生成方法填充到方法区,就会出现方法区溢出。
代理(设置-XX:PermSize=5M -XX:MaxPermSize=5M):
package com.su.mybatis.oracle.controller;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class Test {
private static int num = 0;
public static void main(String[] args) {
try {
while (true) {
num++;
if(num > 500) {
return;
}
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PermGenTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
PermGenTest p = (PermGenTest) enhancer.create();
p.print();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("num:" + num);
}
}
}
class PermGenTest {
public void print() {
System.out.println("----哈哈哈----");
}
}
输出结果:
...
----哈哈哈----
----哈哈哈----
----哈哈哈----
org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
num:305
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:345)
at org.springframework.cglib.proxy.Enhancer.generate(Enhancer.java:492)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
at org.springframework.cglib.proxy.Enhancer.create(Enhancer.java:305)
at com.su.mybatis.oracle.controller.Test.main(Test.java:29)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:413)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336)
... 6 more
Caused by: java.lang.OutOfMemoryError: PermGen space
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
... 11 more
设置-XX:PermSize=10M -XX:MaxPermSize=10M
输出结果:
...
----哈哈哈----
----哈哈哈----
----哈哈哈----
----哈哈哈----
num:501
4、直接内存溢出
直接内存应用在软硬件io操作中,当申请直接内存大于实际的直接内存时,出现直接内存溢出。
使用ByteBuffer申请5M直接内存,设置-XX:MaxDirectMemorySize=1M
package com.su.mybatis.oracle.controller;
import java.nio.ByteBuffer;
public class Test {
public static void main(String[] args) {
ByteBuffer bf = ByteBuffer.allocateDirect(1024*1024*5);//需要5M直接内存
System.out.println(bf.isDirect());
}
}
输出结果:
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:658)
at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:306)
at com.su.mybatis.oracle.controller.Test.main(Test.java:8)
设置-XX:MaxDirectMemorySize=10M
输出结果:
true
内存泄漏
内存泄漏,Memory Leak。程序分配到内存后,未释放或无法释放所有分配的内存,就会出现内存泄漏。内存泄漏会浪费系统内存,导致程序运行慢,严重时系统崩溃。内存泄漏极有可能导致内存溢出。
直接拿AQS中方法举例:
将节点置为null,GC回收,JAVA底层很多防止内存泄漏的写法。
怎么样避免内存溢出和内存泄漏?
1)、部分问题可以通过修改配置解决;2)、检查自己代码,绝大部分情况下是你写的代码导致的。
如果有写的不对的地方,请大家多多批评指正,非常感谢!