也学习Java/JVM/GC
一、JVM 的内存结构;
二、JVM的堆结构及参数配置
三、内存分配;
四、GC的日志;
简言
java 程序可以使用2种方式启动,client模式和server模式。server模式尽可能的加快运算速度,server模式的目的是服务于长期运行的服务端系统,用启动速度和占用内存这两个方面来比较,server模式更倾向于服务的运算速度。client模式更加倾向于启动速度和减少内存的使用,这样就使得client更适合于GUI的使用。
对开发人员来说,更倾向于面向服务器,因此使用最多的还是server模式。
一、JVM的内存结构
通常所说的JVM内存结构都是根据JVM运行时的数据划分的区域。有些数据是在JVM启动时产生的,当JVM停止时数据销毁。而有些数据是由线程产生的,当线程终止时数据销毁。
JVM结构由以下6部分组成:
PC寄存器;
JVM栈;
堆;
方法区;
本地方法区;
运行时常量池;
内存结构如下图所示:
1、PC寄存器
JVM支持多线程并发执行。每个JVM的线程都有自己的PC寄存器(程序计数器),在任意时刻,JVM会保留每个线程正在执行方法的地址。如果不是本地方法,PC寄存器保存着正在执行的JVM指令的地址。如果被执行的方式是本地方法,在JVM的pc寄存器中没有记录。JVM的pc寄存器足以容纳特定平台上的返回地址或者本地指针。
2、JVM栈
每个JVM线程都有和线程同时创建的私有JVM栈,一个JVM栈保存着栈帧。一个JVM栈类似于传统语言的栈,例如C语言的栈:它持有内部变量和部分结果、方法调用和返回值的信息。JVM栈不能被人为期望的控制入栈和出栈。JVM的栈在内存中不需要是连续的。
JVM的栈或者可以是固定大小或者是根据计算的需求动态扩展。如果JVM的栈是固定大小,每个JVM栈的大小可以在创建的时候设置大小。
下面两个异常和JVM的栈相关:
如果线程的运算需要一个超过规定的更大的JVM栈,JVM将会抛出StackOverflowError。
如果JVM栈能被动态的扩展,并且试图的扩展,但是没有足够的内存来满足扩展,或者没有足够的内存来创建线程JVM栈的初始化,JVM将会抛出OutOfMemoryError。
3、堆
JVM的堆是被所有JVM的线程共享的。堆是内存为所有类实例和数组在运行时分配的。
堆是在JVM启动时创建的。堆存储的对象是被自动存储管理系统回收的(熟知的垃圾收集器);对象从来不是被明确释放的。JVM没有给出特定类型的自动存储管理系统,存储管理系统可以根据特定的系统需求来选择。堆可以是固定大小后或者如果不能得到一个大的堆,可以根据计算的需要扩展。堆的内存可以不是连续的。
下面是和堆有关的异常条件:
如果运算需要的堆大小超过了自动存储管理系统的分配,JVM将会抛出OutOfMemoryError。
4、方法区(永久代)
JVM的方法区被所有线程共享。方法区类似于传统语言的存储编译代码的区域,或者类似于操作系统的 文本 段。它存储了每个类的结构,例如常量池,字段和方法数据,方法和构造函数的代码,包括在类中专用的方法,初始化实例和接口实例。
方法区在JVM启动的时候创建。尽管方法区在逻辑上属于堆的一部分,但是最简单的实现方法是不回收和压缩这个区域(因为此区域的数据不需要回收)。这个版本(1.7)的JVM说明没有强制方法区的位置和用来管理代码的策略。方法区的大小可以是固定的,当不能够得到一个大的方法区时,方法去可以被运行时扩展。方法区的内存可以不是连续的。
下面是和方法区关联的异常。
如果在方法区中的内存不能满足需求的分配,JVM会抛出OutOfMemoryError。
5、运行时常量池
运行时常量池是在一个类文件中的每个类或者接口运行时的常量池表。它包含了几种类型的常量,范围从编译时就知道的数字常量到运行时必须被解析的方法和域引用。运行时的常量池类似传统语言的符号表,尽管它比典型的符号表包含了更广阔的数据范围。
每个运行时常量池都是从JVM的方法区被分配,即属于方法区的一部分(上图中为了区分开,把方法区和运行时常量池分开)。当JVM被创建的时候接口或类的运行时常量池被构造。
下面的异常和运行时的常量池有关:
当正在创建一个类或接口,如果运行时常量池需要更多内存比从JVM的方法区得到的多,JVM会抛出OutOfMemoryError。
6、本地方法栈
JVM使用传统语言的栈实现了本地方法栈,口语为“C stacks,” 支持本地方法(方法不是用java语言实现的)。本地方法栈可能被用其它语言来实现JVM的指令集的解释程序,例如C。如果没有提供本地方法栈,JVM实现就不能加载本地方法,并且也不能依赖于传统的方法栈。如果需要提供,本地方法栈通常在线程创建的时候分配。
JVM文档允许本地方法栈有确定的大小或者在运行时动态扩展。如果本地方法栈固定大小,在栈创建的时候每个本地方法的大小可以独立的设置。
和本地方法栈相关的异常如下:
如果一个正在运行的线程需要的本地方法栈超出了允许的大小,JVM会抛出StackOverflowError。
如果本地方法栈能够动态的扩展,当本地方法栈在扩展时没有足够的内存满足,或者一个新的线程创建时没有足够的内存来完成初始化本地方法栈,JVM会抛出OutOfMemoryError。
参考:https://docs.oracle.com/javase/specs/
参考:《深入分析Java Web技术内幕》 许令波