一、堆栈溢出错误模拟-StackOverflowError
1.1 代码
/**
* 堆栈溢出错误模拟
*
* @author sunzhiqiang
* @date 2021/12/01 19:19:36
*/
public class _1_StackOverflowErrorMock {
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
stackOverflowError();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
int num = 0;
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num);
num++;
}
}).start();
new CountDownLatch(1).await();
}
private static void stackOverflowError() {
try {
TimeUnit.NANOSECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stackOverflowError();
}
}
1.2 分析
最终打印:
线程1代码抛出异常了,线程2还会运行:
——这里的栈溢出,只是一个栈帧装不下;
代码地址(3_memory):
msb-debug
二、堆内存泄漏模拟,mat分析
Java笔记:MAT使用详解
一文深度讲解JVM 内存分析工具 MAT及实践(建议收藏)
2.1 模拟代码
/**
* 内存泄漏模拟
* jmap -dump:format=b,file=D:/workspace/mashibing/msb-debug/dump.hprof pid
*
* @author sunzhiqiang
* @date 2021/11/26 21:56:42
*/
@Data
public class MemoryLeakMock {
private List<Object> list = new ArrayList<>();
public static void main(String[] args) throws InterruptedException {
MemoryLeakMock memoryLeakMock = new MemoryLeakMock();
long num = 1 * 1024 * 1024;
while (num > 0) {
memoryLeakMock.getList().add(new byte[1024]);
num--;
}
new CountDownLatch(1).await();
}
}
2.2 分析
t线程1代码抛出异常了,线程2还会运行;
2.3 导出dump文件
jmap -dump:format=b,file=E:/Work_Space/Mca_Space/msb-debug/dump.hprof pid
2.4 mat分析dump文件
2.4.1 Leak Suspects分析
疑惑:生成的dump.hprof文件,与课上的不一样;
课上的:
我的:没有提示MemoryLeakMock类的这条;
猜测可能跟jvm启动参数有关。
除了mat分析工具,还有jprofile工具,只不过是收费的。
三、元数据空间内存溢出-MataspaceOOMTest
3.1 代码
/**
* JVM参数
* -XX:MetaspaceSize=12m -XX:MaxMetaspaceSize=12m
* <p>
* java 8及之后的版本使用Metaspace来替代永久代,存放了以下信息:
* <p>
* 虚拟机加载的类信息
* 即时编译后的代码
* <p>
* 模拟Metaspace空间溢出
**/
public class _3_MataspaceOOMTest {
public static void main(String[] args) {
new Thread(() -> {
try {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMTest.class);
enhancer.setUseCache(false);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invoke(o, args));
enhancer.create();
}
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}).start();
new Thread(() -> {
int num = 0;
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num);
num++;
}
}).start();
}
static class OOMTest {
}
}
3.2 分析
window系统未成功复现此异常,不知具体什么原因。
四、不能创建线程异常-java.lang.OutOfMemoryError:unable to create new native thread
4.1 代码
/**
* 高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError:unable to create new native thread
* native thread异常与对应的平台有关
* <p>
* 导致原因:
* 1 应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限
* 2 服务器并不允许应用程序创建这么多线程,linux系统默认允许单个进程可以创建的线程数是1024个,
* 应用创建超过这个数量,就会报java.lang.OutOfmemoryError:unable to create new native thread
* <p>
* 解决办法:
* 1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数量降到最低
* 2.对于有的应用,确实需要创建很多线程,远超过linux系统的默认1024个线程的限制,可以通过修改linux服务器配置,扩大linux默认限制
**/
public class _4_UnableCreateNewThreadDemo {
public static void main(String[] args) {
for (int i = 0; ; i++) {
System.out.println("********i=" + i);
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "" + i).start();
}
}
}
五、java.lang.OutOfMemoryError:Direct buffer memory
5.1 代码
/**
* 〈一句话功能简述〉<br>
* 配置参数:
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
* <p>
* 故障现象
* Exception in thread "main" java.lang.OutOfMemoryError:Direct buffer memory
* <p>
* 导致原因:
* 写NIO程序经典使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的 I/O方式,
* 可以使用native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
* 这样能在一些场景种显著提高性能,因为避免了在Java堆和Native堆种来回复制数据。
* <p>
* ByteBuffer.allocate(capability) 第一种方式是分配JVM堆内存,
* ByteBuffer.allocateDirect(capability)第一种方式时分配OS本地内存,由于不需要内存拷贝所以速度相对较快。
* <p>
* 但如果不断分配本地内存,对内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。
* 这时候对内存重组,但本地内存可能已经使用光了,再次尝试分配本地内存就会出OutOfMemoryError,那程序就直接崩溃了。
**/
public class _5_DirectBufferMemoryDemo {
public static void main(String[] args) {
System.out.println("配置的maxDirectMemory:" + (sun.misc.VM.maxDirectMemory() / (double) 1024 / 1024) + "MB");
ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
六、GC overhead limit exceeded
6.1 代码
/**
* 〈一句话功能简述〉<br>
* <p>
* JVM参数配置演示
* -Xms20m -Xmx20m -XX:+PrintGCDetails
* <p>
* GC 回收实践过程时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存
* 连续多次GC都只会收到了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit错误会发生什么情况呢?
* <p>
* 那就是GC清理的这么点内存很快会再次填满,迫使GC再次执行,这样就形成了恶性循环,
* CPU使用率一直100%,而GC却没有任何成果
**/
public class _6_GCOverheadDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
try {
while (true) {
list.add(String.valueOf(++i).intern());
}
} catch (Throwable e) {
System.out.println("*******i:" + i);
e.printStackTrace();
throw e;
}
}
}
参考:OutOfMemoryError系列(2): GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded问题分析及解决
七、其他
-
慢sql为什么影响应用?
——占用连接数,导致资源无法释放。
慢sql的累计,最先会导致数据库服务器cpu飙高。一般数据库cpu利用率都不会超过20%。如果超过20%,一般来说,肯定是有慢sql了,监控慢sql,最方便的就是配一个慢sql日志,每天查一下; -
一般情况下,内存溢出,只是异常,不会使jvm进程死掉。
-
rpc接口,传参-2M容量;
-
为什么阿里代码规范,代码不能超过80行?为什么尽量减少递归?
——因为栈没有执行完的时候,栈上所有方法不能释放。如果不释放,恰巧来了请求,内存达到一定阈值,会出发gc。
参考文章:
为什么说递归效率低?
为何java不推荐使用递归?