1.了解了监视工具以后就来分析一些小案例
先来体验一下堆内存溢出的感觉。对内存默认是分很多的,很难溢出的,所以我们要设置一下起始堆内存分配的大小。设置最大堆内存为50M,初始堆内存为50M,垃圾回收器使用单线程垃圾回收器。
package com.wx.jvm;
import java.util.ArrayList;
import java.util.List;
public class TestMemory {
public static void main(String[] args) {
try {
Thread.sleep(10000);
fillHeap(100);
Thread.sleep(10000);
}catch (Exception e){
e.printStackTrace();
}
}
private static void fillHeap(int num) throws Exception{
List<OOMObject> oomObjects=new ArrayList<OOMObject>();
for (int i=0;i<num;i++){
Thread.sleep(50);
oomObjects.add(new OOMObject());
}
System.gc();
}
private static class OOMObject {
//64k的大小
public byte[] placeholder=new byte[64*1024*20];
}
}
运行:
2.测试一下线程,可以监控到死循环的线程和处于等待状态的线程
package com.wx.jvm;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TestThread {
/**线程死循环演示*/
public static void createBusyThread(){
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("进入死循环的线程");
while (true);
}
},"createBusyThread");
thread.start();
}
/**线程锁等待*/
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("createLockThread");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"createLockThread");
thread.start();
}
public static void main(String[] args) throws Exception{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
Object object = new Object();
createLockThread(object);
}
}
启动死循环的线程:
进入处于等待状态的线程:
可以清楚的看到线程是在26行处处于等待状态
3.案例三:检测死锁
这个代码常见了一百个线程,但是锁只有两把,当a=1时,线程拿到的是Integer的值为1的对象的锁,此时试图去拿Integer的值为2的对象的锁执行下一个步骤,但是如果另一个线程a=2,即他已经拿到Integer的值为2的对象的锁,那么就已经出现了死锁。
package com.wx.jvm;
public class TestDeadThread implements Runnable {
int a, b;
public TestDeadThread(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new TestDeadThread(1, 2)).start();
new Thread(new TestDeadThread(2, 1)).start();
}
}
}
启动:
ok,这已经检测到死锁了。这下可以把线程dump出来,得到一个非常详细的报告。
下面使用jconsole查看一下,他有专门检测死锁的:
4.案例四:当堆内存溢出的时候dump这个错误到指定的文件位置,以便后续的分析
参数设置:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=f:/dump
代码:
package com.wx.jvm;
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
static class OOMObject{
public String[] stringArray=new String[32*1024];
}
public static void main(String[] args) {
List<OOMObject> list=new ArrayList<OOMObject>();
while (true){
list.add(new OOMObject());
}
}
}
运行:
用工具打开:
分析可疑内存溢出问题:
概况:
查看内部对象的树:
5.案例5:栈内存溢出演示,可以疯狂创建线程即可使栈内存溢出
这个我就不试了,读者可以试试。
public class JavaVMStackOOM {
private void dontStop() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) throws Throwable {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
6.案例6:找溢出,和上一个不一样这个是栈的深度太深,栈帧出不来。
package com.wx.jvm;
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
7.案例7:本机直接内存溢出
什么是直接内存,就是JVM做本地调用的时候,用到JVM之外的内存。如果机器剩余的内存比较小了,JVM做本地方法调用就会可能会使本机直接内存溢出。
参数设置:-Xmx20M -XX:MaxDirectMemorySize=10M
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
直接内存不归JVM管,也就是说GC不会回收这个内存,进一步说就是如果我们自己管好这部分的内存效率是非常高的。