JVM基础
Java堆(Heap)
- 线程共享的,或者说是被栈共享
- 用来存放new的对象和数组,抛异常OutOfMemoryError
- 运行时动态分配内存,速度慢,但灵活(为对象分配内存的方式:指针碰撞、空闲列表)
指针碰撞:内存规整,一般用在Serial,PaeNew等垃圾收集器中,即堆中的新生代中
空闲列表:内存不规整,一般用在基于Mark-Sweep的CMS垃圾收集器,即堆中的老年代中 - 不连续的内存区域
- 有Java虚拟机的自动垃圾回收器(GC)来管理
- 堆又可以分为新生代和老年代,实际还有一个区域叫永久代,但jdk1.7就已经没有了。
1、永久代是当jvm启动时就存放的JDK自身的类和接口数据,关闭则释放。
2、新生代可以分为Eden区和两个幸存区(Survivor),使用复制回收算法
Java栈(Stack)
- 线程私有,每个线程包含一个栈
- 用来存放基本类型变量,局部变量,对象的引用;
- 系统自动分配与回收内存,效率较高,快速,存取速度比堆要快;
- 是一块连续的内存的区域,有大小限制,超出会栈溢出,抛异常StackOverflowError
- Java会自动释放掉为该变量所分配的内存空间
方法区
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
- 方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
- 空间不足则会引发OutOfMemoryError。
常见问题
1、堆空间被占满
异常: java.lang.OutOfMemoryError: Java heap space
由于所有堆空间都被无法回收的垃圾对象占满,虚拟机无法再在分配新空间。
解决:
根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,找到泄漏点。
2、持久代被占满
异常:java.lang.OutOfMemoryError: PermGen space
Perm空间被占满。无法为新的class分配存储空间而引发的异常。Java反射比较常见。主要原因就是大量动态反射生成的类不断被加载,最终导致Perm区被占满。
解决: -XX:MaxPermSize=512m
3、堆栈溢出
异常:java.lang.StackOverflowError
说明:一般是递归没返回,或者循环调用造成
4、线程堆栈满
异常:Fatal: Stack size too small
说明:java中一个线程的空间大小是有限制的。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上面异常。
解决:增加线程栈大小。-Xss2m。但此配置无法解决根本问题,还要看代码是否有造成泄漏的部分。
5、系统内存被占满
异常:java.lang.OutOfMemoryError: unable to create new native thread
说明:
这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,就出现这个异常了。
解决:
1. 重新设计系统减少线程数量。
2. 线程数量不能减少的情况下,通过-Xss减小单个线程大小。以便能生产更多的线程