为什么要了解JVM
作为java 程序员开发,如果想要更好的优化程序的话,必须要对jvm的整个原理非常了解,这样才能在高并发的场景中,根据自己的业务需求去配置相应的jvm。
jvm内存分配
从功能角度来考虑jvm内存划分可以分为:
- 堆: 主要是创建对象或者数组分配的内存区。也是java里最大的一块内存区,也经常会gc回收垃圾对象释放内存。
- 栈: 这里分为虚拟机栈和本地方法栈。
- 虚拟机栈:主要是线程执行一个方法的时候会单独分配的一块内存(存放局部变量,执行的方法一些元信息等等),这也是方法天然线程安全的原因。(方法的进入和退出对应的栈帧的入栈和出栈)
- 本地房发栈:在jvm有些方法比较特殊是native方法,这类方法一般是调用本地系统接口,执行的时候没有加载字节码这一过程,所以执行也与普通的方法不同。
- 非栈:主要分为方法区和常量池。
- 方法区:class在被加载的时候,方法的原始数据会存在在这个内存区。基本上不会存在被回收的可能。因为它属于模板数据,虚拟机栈里运行线程会加载方法区的方法元数据。
- 常量池: 主要存放jvm加载字节码的时候,static区分配的内存。当然运行以后也可以继续向常量池增加数据,例如: String 里的 intern()方法,可以将字符创放入常量池。
- 程序计数器区: 主要是记住线程指令的地址。当还没执行的线程,记录的是起始地址。运行的线程记录的是当前执行指令的地址。(记录后便于cpu时间片切换)
注: 程序计数器、虚拟机栈、本地方法栈 随着线程而生,随着线程结束而灭。
常用的jvm参数
- 设置堆的大小(最大):
-Xmx10M
- 设置堆的大小(最小):
-Xmn10M
- 设置显示gc回收日志:
-verbose:gc
- 让虚拟机的内存异常日志打印出来(会有一个hrrof的文件):
-XX:+HeapDumpOnOutOfMemoryError
- 栈容量设置:
-Xss10M
线程OOM(java.lang.OutOfMemoryError)
当 -Xss10M
固定的时候,每启动一个线程的时候就会占用虚拟机栈的空间,所以每个线程分配的虚拟机栈的容量越大,那么能够启动的线程数就越少。
代码演示:
/**
* <p>
* 测试虚拟机栈 java.lang.OutOfMemoryError
* </p>
*
* @author robinyang
* @date 2018.10.03
*
* @see StackOverflowError
*/
public class Test04 {
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new Thread(new OOMThread()).start();
System.out.println(i);
}
}
public static class OOMThread implements Runnable {
public void run() {
byte[] bytes = new byte[10240];
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
当启动七百多的时候就会出现:
Exception in thread "Thread-757" Exception in thread "Thread-761"
Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Exception in thread "Thread-758" Exception in thread "Thread-760" Exception in thread "Thread-762" Exception in thread "Thread-763" *** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message can't create byte arrau at JPLISAgent.c line: 813
增强类导致OOM
方法区一般存放的是Class相关信息,比如 类名、访问修饰符、常量池、字段描述、方法描述等。想要测试这些区域OOM 可以通过产生大量类去填充方法区即可。可以通过动态反射和CGLib可以产生大量的动态类。
总结:在一些框架,如:Spring
、Hibernate
等等,在对类进行增强的时候,经常会产生很多类,类越多,导致方法区的内存占用比较大,出现OOM。还有jvm上的动态语言为了实现动态特性会持续创建类,也会导致方法区内存占用过大,出现OOM。
直接内存默认值
DirectMemory
这块内存跟元数据的直接内存是两块不一样的内存,这里的内存可以存放我们程序运行时的数据。当我们不设置直接内存大小的时候,默认和Java堆内存一样大小 -Mmx5M
一样大。
当然,我们可以直接设置堆外内存: -XX:MaxDirectMemorySize=10M
网友评论