Java虚拟机在执行Java程序时,会把它所管理的内存区域在逻辑上划分为不同的区,具体如下图:
堆(Heap)
堆内存是被所有线程共享(因此在堆上分配内存需要加锁)的一块内存区域,主要用来存放对象的实例,垃圾回收也主要是回收堆的空间。
现在收集器基本都采用分代收集算法,Java堆还可以细分为:新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation 也被成为方法区)。
相关参数:
-Xms 设置堆内存初始化大小
-Xmx 设置堆内存最大值
当堆中没有可分配的内存并且无法扩展时,将会抛出OutOfMemoryError(OOM)异常- 新生代(Young Generation)
新生代又分为Eden空间和Survivor空间,Survivor空间又包括From Survivor空间和To Survivor空间两个相等的逻辑区域。为了提高效率,JVM会为每个线程在Eden空间分配一块私有的分配缓冲区空间(Thread Local Allocation Buffer)TLAB,大小默认为Enen的1%。
新生代的对象基本都是”朝夕生死的”,所以在新生代的垃圾回收算法采用的是复制算法,对象优先在Eden区中分配,当Eden区满的时候(名为From Survivor区也满,To Survivor区为空),虚拟机将发动一次Minor GC,此时,Eden区中存活的对象被复制到”To Survivor“区,”From Survivor“区存活的,年龄到达一定阀值的对象被移动到老年代,没有达到的被复制到”To Survivor“区
相关参数:
-Xmn 设置新生代内存大小
-XX:SurvivorRatio 设置Eden和Survivor空间的大小比例(默认为 8:1)
-XX:PretenureSizeThreshold – 设置超过指定大小的大对象直接分配在老年代中 - 老年代(Old Generation)
老年代采用”标记-整理算法“,老年代的对象都没那么容易死。
相关参数:
-XX:MaxTenuringThreshold – 设置对象在新生代中存活的次数 - 永久代(PermanentGeneration)
又称为方法区,被线程共享,用来存放JVM加载的类型信息。包括: 类型基本信息,常量池,字段信息,方法信息,类变量,指向ClassLoader的引用,Class类的引用,方法表等。方法区是全局共享的,在一定条件下也会被GC
相关参数 :
-XX:PermSize –设置Perm区的初始大小
-XX:MaxPermSize –设置Perm区的最大值
- 新生代(Young Generation)
栈(Stack)
Java虚拟机栈是线程私有的,它的生命周期和线程相同,栈描述的是Java方法执行的内存模型:方法执行时创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息,一个方法的调用直到完成,对应着一个栈帧在虚拟机栈的入栈和出栈的过程。
相关参数:
-Xss –设置方法栈的最大值程序计数器
程序计数器也是线程私有的,是一块较小的内存空间,可看作当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。本地方法栈
本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。- 直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但这部分内存也被频繁的使用,也可能导致OutOfMemoryError异常。
在JDK1.4中新加入的NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
本机直接内存的分配不受Java堆大小的限制,既是内存,还是受本机总内存大小和处理器寻址空间的限制。在配置虚拟机参数时,根据实际内存设置-Xmx等参数信息,忽略直接内存,使得各个内存区域总和大于物理内存的限制,从而导致动态扩展时出现OutOfMemoryError异常。
Java被设计成一个安全,可管理的环境,然而 Java HotSpot有一个后门,提供了对低级别的,对直接内存和线程的操作。这个后门是—-sun.misc.Unsafe。这个类在JDK中有广泛的应用,例如,java.nio和java.util.concurrent