这篇文章主要是记录自己做的一些小的测试.主要包括内存溢出,栈溢出,以及死锁问题.
PS:文章中使用了Arthas
工具,用来动态监控JVM的一些资源,非常好用,强烈安利一下.
OOM
OutOfMemory ,内存不够用了,一般是什么原因呢?
给应用程序分配的内存太小,只能通过增大内存来解决.
内存泄漏.有一部分内存”无用”了,但是因为编码问题导致的没有被垃圾回收掉,产生了泄漏,最终导致了内存溢出(OOM).
我们来手动写一个OOM.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package javatest;
import java.util.HashMap; import java.util.Map;
public class OOMTest {
static class Key{ int id;
public Key(int i){ int id; }
@Override public int hashCode() { return this.id; } }
public static void main(String [] args ) {
Map<Key, String> testMap = new HashMap<>(); while (true) { for (int i = 0; i < 10000; i++) { if (!testMap.containsKey(new Key(i))) { testMap.put(new Key(i), "Number:" + i); } } } }
}
|
注意:直接运行可能得到OOM有点慢,可以设置一下Xmx参数小一点.
实现原理:不断的new出新的对象,并将它们放进一个Map里面保持引用.这样一直无法回收,终会OOM.
同时,也可以通过一些工具,比如阿里的Arthas
来动态的监控JVM的内存使用情况,如下图.
是可以肉眼看到内存使用量以及占用率不断上升的.
StackOverFlow
栈溢出,首先和内存溢出一样,我们考虑一下栈里面放的是什么?
执行方法时的一些调用环境(比如参数及局部变量),那么什么时候会栈溢出呢?
无限的递归,相当于你的参数无限多,那么栈放不下.
局部变量太大了,正常分配的栈空间(1M)不够用.
我们用递归来实现一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package javatest;
public class StackOverFlowTest {
public static void main(String[] args) {
new StackOverFlowTest().fun(10); }
public int fun(int n) { return fun(n); }
}
|
所以当发生StackOverFlow的时候,记得检查一下递归调用的结束条件.
死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
造成死锁的条件有四个:
互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
手动写一个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package javatest;
public class DeadLockTest {
public static void main(String[] args) { Sy sy = new Sy(0); Sy sy2 = new Sy(1); sy.start(); sy2.start(); } }
class Sy extends Thread {
private int flag;
static Object x1 = new Object(); static Object x2 = new Object();
public Sy(int flag) { this.flag = flag; }
@Override public void run() { System.out.println(flag); try { if (flag == 0) { synchronized (x1) { System.out.println(flag + "锁住了x1"); Thread.sleep(1000); synchronized (x2) { System.out.println(flag + "锁住了x2"); } System.out.println(flag + "释放了x1和x2"); } } if (flag == 1) { synchronized (x2) { System.out.println(flag + "锁住了x2"); Thread.sleep(1000); synchronized (x1) { System.out.println(flag + "锁住了x1"); } System.out.println(flag + "释放了x1和x2"); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
|
实现原理:
定义一个类,类有两个静态对象对象,创建一个对象去锁住1,然后请求对2加锁.
再创建一个对象锁住2,然后请求对1加锁.
使用的锁为synchronized关键字.
然后使用监控工具查看当前jvm的线程,可以发现main方法中启动的两个线程阻塞住了.而且一直也无法释放.
完.
ChangeLog
2019-03-06 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见个人博客——>呼延十