笔者将从概念上介绍 Java 虚拟机内存的各个区域,讲解这些区域的做用、服务对象以及其中可能产生的问题服务器
运行时数据区域主要有:markdown
其中方法区和堆由全部线程共享,其他为线程内部隔离的数据区域。多线程
一块较小的内存区域,能够看作是当前线程所执行的字节码的行号指示器。ide
因为多线程的切换时经过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个肯定的时刻,一个处理器(内核)都只会执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置,各线程间必须各自拥有一个独立的程序计数器,即线程私有
内存。函数
另外,执行Java方法的时候,计数器记录的是正在执行的虚拟机字节码指令的地址;而执行的是Native方法,记录的值为空。此内存区域是Java虚拟机规范中惟一一个没有规定任何OutOfMemoryError
状况的区域。性能
虚拟机栈描述的是Java方法执行的内存模型。和程序计数器同样,也是线程私有
的。每一个方法在执行的同时会建立一个栈帧
。atom
栈帧
用于存储:局部变量表、操做数栈、动态链表、方法出口灯信息。每一个方法从调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中入栈到出栈的过程。线程
局部变量表
用于存放编译期可知的各类基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,不等同于对象自己,多是一个指向对象的起始地址的引用指针,也多是个指向一个对象的句柄或其余与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)指针
该区域根据Java虚拟机规范中,定义了两种异常状况:code
StackOverflowError
OutOfMemoryError
它的做用与虚拟机栈类似,区别在于,前者为虚拟机栈提供Native方法服务,后者为虚拟机执行Java方法(字节码)服务。
该区域和虚拟机栈抛出的异常状况同样。
Java 堆是Java虚拟机所管理的内存中最大的一块。被全部线程共享的一块区域,几乎全部的对象实例都在这里分配内存。也是垃圾收集器管理的主要区域,所以也被称为GC堆
。
从内存回收的角度,能够细分为:新生代和老年代;更细致些的划分:Eden空间、From Survivor空间、To Survivor空间。
从内存分配的角度来看,线程共享的Java堆中可能划分出多个线程的私有分配缓冲区(Thread Local Allocation Buffer,TLAB)。
根据Java虚拟机规范的规定,Java堆能够在物理上处于不连续的内存空间中,只要逻辑上连续就能够了。在实现时,能够实现为固定大小的,也能够是可扩展的。
通常都是可扩展的,经过参数进行配置:-Xmx
和 -Xms
若是在堆中没有内存完成实例分配,而且堆也没法在扩展时,将会抛出OutOfMemoryError
。
方法区和Java堆同样,是各个线程共享的内存区域,用于存储已被虚拟机栈加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区在Java虚拟机规范中被描述为一个逻辑部分,可是它有个别名叫作Non-Heap
非堆,目的就是和Java堆区分开。
该区域垃圾回收的主要目标是:针对常量池的回收和类型的卸载。
根据Java虚拟机规范,当方法区没法知足内存分配需求时,将抛出OutOfMemoryError
.
方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池。用于存放编译器生成的各类字面量和符号引用,这部份内容将在类加载后进入方法区的运行时常量池中存放。
既然属于方法区的一部分,异常抛出和方法区一致。
直接内存(Direct Memory)并非虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。可是这部分却被频繁使用,也可能会致使OutOfMemoryError
出现。
JDK1.4后引入的NIO,引入了一中基于通道与缓冲区的I/O方式,可使用Native函数库直接分配堆外内存,而后经过一个存储在Java堆中的DirectByteBuffer对象做为这块内存的引用进行操做。这样作是为了在一些场景中提升性能,避免Java堆和Native堆中来回复制数据。
可是,该内存的分配会受到本地内存总量的限制,服务器管理员在配置虚拟机参数时,能够根据实际内存设置-Xmx
等参数信息来调整堆的内存容量,来控制直接内存能够分配的最大容量。
《深刻理解Java虚拟机-JVM高级特性与最佳实践》读书笔记