写点什么

JVM 内存溢出分析:堆内存溢出 + 虚拟机,BTAJ 大厂最新面试题汇集

用户头像
极客good
关注
发布于: 刚刚

/**


设置每个线程的栈大小:-Xss256k


运行时,不断调用 doSomething()方法,main 线程不断创建栈帧并入栈,导致栈的深度越来越大,最终导致栈溢出。


*/


public class StackSOF {


private int stackLength=1;


public void doSomething(){


stackLength++;


doSomething();


}


public static void main(String[] args) {


StackSOF stackSOF=new StackSOF();


try {


stackSOF.doSomething();


}catch (Throwable e){//注意捕获的是 Throwable


System.out.println("栈深度:"+stackSOF.stackLength);


throw e;


}


}


}


上述代码执行后抛出:


Exception in thread “Thread-0” java.lang.StackOverflowError 的异常。


(2)OutOfMemoryError:如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出 OutOfMemoryError。


我们可以这样理解,虚拟机中可以供栈占用的空间≈可用物理内存 - 最大堆内存 - 最大方法区内存,比如一台机器内存为 4G,系统和其他应用占用 2G,虚拟机可用的物理内存为 2G,最大堆内存为 1G,最大方法区内存为 512M,那可供栈占有的内存大约就是 512M,假如我们设置每个线程栈的大小为 1M,那虚拟机中最多可以创建 512 个线程,超过 512 个线程再创建就没有空间可以给栈了,就报 OutOfMemoryError 异常了。



栈上能够产生 OutOfMemoryError 的示例如下:


/**


设置每个线程的栈大小:-Xss2m


运行时,不断创建新的线程(且每个线程持续执行),每个线程对一个一个栈,最终没有多余的空间来为新的线程分配,导致 OutOfMemoryError


*/


public class Sta


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


ckOOM {


private static int threadNum = 0;


public void doSomething() {


try {


Thread.sleep(100000000);


} catch (InterruptedException e) {


e.printStackTrace();


}


}


public static void main(String[] args) {


final StackOOM stackOOM = new StackOOM();


try {


while (true) {


threadNum++;


Thread thread = new Thread(new Runnable() {


@Override


public void run() {


stackOOM.doSomething();


}


});


thread.start();


}


} catch (Throwable e) {


System.out.println("目前活动线程数量:" + threadNum);


throw e;


}


}


}


上述代码运行后会报异常


在堆栈信息中可以看到 java.lang.OutOfMemoryError: unable to create new native thread 的信息,无法创建新的线程,说明是在扩展栈的时候产生的内存溢出异常。


总结:在线程较少的时候,某个线程请求深度过大,会报 StackOverflow 异常,解决这种问题可以适当加大栈的深度(增加栈空间大小),也就是把 -Xss 的值设置大一些,但一般情况下是代码问题的可能性较大;在虚拟机产生线程时,无法为该线程申请栈空间了。


会报 OutOfMemoryError 异常,解决这种问题可以适当减小栈的深度,也就是把 -Xss 的值设置小一些,每个线程占用的空间小了,总空间一定就能容纳更多的线程,但是操作系统对一个进程的线程数有限制,经验值在 3000~5000 左右。


在 jdk1.5 之前 -Xss 默认是 256k,jdk1.5 之后默认是 1M,这个选项对系统硬性还是蛮大的,设置时要根据实际情况,谨慎操作。


3、方法区溢出


前面说到,方法区主要用于存储虚拟机加载的类信息、常量、静态变量,以及编译器编译后的代码等数据,所以方法区溢出的原因就是没有足够的内存来存放这些数据。


由于在 jdk1.6 之前字符串常量池是存在于方法区中的,所以基于 jdk1.6 之前的虚拟机,可以通过不断产生不一致的字符串(同时要保证和 GC Roots 之间保证有可达路径)来模拟方法区的 OutOfMemoryError 异常;但方法区还存储加载的类信息,所以基于 jdk1.7 的虚拟机,可以通过动态不断创建大量的类来模拟方法区溢出。


/**


设置方法区最大、最小空间:-XX:PermSize=10m -XX:MaxPermSize=10m


运行时,通过 cglib 不断创建 JavaMethodAreaOOM 的子类,方法区中类信息越来越多,最终没有可以为新的类分配的内存导致内存溢出


*/


public class JavaMethodAreaOOM {


public static void main(final String[] args){


try {


while (true){


Enhancer enhancer=new Enhancer();


enhancer.setSuperclass(JavaMethodAreaOOM.class);

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
JVM内存溢出分析:堆内存溢出+虚拟机,BTAJ大厂最新面试题汇集