向一个集合里添加很多数据这种OOM的例子就不说了
1.内存飙升频繁FullGC
内存飙升频繁FullGC又没有发生OOM很明显这个对象在年轻代没有被销毁进入了老年代,
至于为什么不会OOM是因为这个对象的生命周期很短比如5秒所以每次fullgc都能回收掉
导致对象没有在年轻代被回收的原因有以下情况该
1.对象的内存比年轻代还大,比如对象100兆,年轻代50兆直接放入老年代
2.触发了动态年龄判断放入老年代
3.触发了空间担保机制放入老年代
4.高并发方法执行慢,产生的内存多年轻代装不下此时进行进行youngGC也回收不掉,
是因为方法没有结束是不能回收方法内的gcroot的,那么此时在进来10个个线程这些线程
产生的数据年轻代装不下那么就放入老年代里,当5秒过后这些方法才执行完那么此时的方法
才出栈如果老年代满了是可以回收掉这些垃圾的,如果并发量在高些就会发生OOM,
比如老年代存放的都是2秒后方法才结束的对象当内存满了进行fullgc是回收不掉的因为这些对象
2秒后才能成为垃圾
4核8线程
-Dfile.encoding=utf-8
-Xms400m
-Xmx400m
-XX:MetaspaceSize=512m
-XX:SurvivorRatio=8
-Xss512k
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=f:/dev/
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:f:/dev/gc.log
情况一 方法执行很快产生的垃圾处理情况
并发量中等但是方法产生的垃圾小
[8并发]:[单个方法耗时40毫秒占用1m]:[是否fullgc false]
并发量中方法产生的垃圾大
[8并发]:[单个方法耗时70毫秒占用2m]:[是否fullgc true]
并发量高但是方法产生的垃圾小
[高并发]:[单个方法耗时40毫秒占用1m]:[是否fullgc true]
情况二 方法执行很慢产生的垃圾处理情况
并发量中但是方法产生的垃小
[8并发]:[单个方法耗时1000毫秒占用1m]:[是否fullgc false]
并发量高但是方法产生的垃小
[高并发]:[单个方法耗时1000毫秒占用1m]:[是否fullgc true]
并发量中方法产生的垃大
[8并发]:[单个方法耗时1000毫秒占用2m]:[是否fullgc true]
并发量高方法产生的垃大
[高并发]:[单个方法耗时1000毫秒占用2m]:[OOM]
总结:方法执行慢并且产生的垃圾多那么就很容易频繁fullgc和oom,而高并发就容易让这个条件满足
2.解决高并发并发fullgc
采用线程池控制并发数量,
没控制并发前
控制并发后
方式一线程池
@RestController
@RequestMapping("/userOrder")
public class UserOrderController {
ExecutorService executorService = Executors.newFixedThreadPool(2);
@RequestMapping("/hello")
public String hello(){
executorService.submit(()->{
//模拟service方法产生100的垃圾,2个线程最高200m垃圾
B b = new B(1);
});
return "success";
}
}
方式二Semaphore (推荐)
@RestController
@RequestMapping("/userOrder")
public class UserOrderController {
static Semaphore semaphore = new Semaphore(10,true);
@RequestMapping("/hello")
public String hello(){
try{
//请求一个信号
semaphore.acquire();
B b = new B(1);
//释放一个信号
semaphore.release();
}catch (Exception e){
e.printStackTrace();
}
return "success";
}
}