jvm01:java内存区域与内存

一、 java虚拟机的启动,一定是我们使用java命令或者javaw命令来启动的。java命令呢,后面会跟上一个启动类,这个类呢会有一个main方法。当我们使用java命令来启动jvm的时候,1.系统第一步做的就是装载配置。也就是系统会在当前路径下去寻找与java版本相匹配的jvm config文件。2.在找到配置文件之后,系统会根据配置文件的指定,寻找到指定的jvm.dll。而这个dll则是java虚拟机的主要实现。3.在找到匹配当前系统版本的dll文件之后呢,它就会使用这个dll去初始化jvm虚拟机去获得相关的接口,例如JNIEnverament接口,这个接口中有着大量的方法,例如去寻找启动jvm的类的方法 findclass()。 4.找到main方法,并运行。

二、对于从事C/C++的程序员而言,在内存管理领域,他们是拥有最高权力的皇帝,但同时又是最底层基础的工作人员。因为他们即拥有每一个对象的所有权,有必须得去管理每个对象

的生老病死。但是对于java程序员而言,由于jvm的自动内存管理机制的存在。不再需要为每一个对象去编写delete/free方法。不容易出现内存溢出和内存泄漏的问题。但是如果不清楚jvm内存管理机制的内部运转,一旦出现内存泄漏或者溢出,将会很难排查。

jvm运行时的各个区域

1.程序计数器,又叫PC寄存器。它是线程独有的。每一个线程在创建的时候,jvm都会为之分配一个PC寄存器。它永远指向该线程中下一条指令的地址。当执行

本地方法,也就是Native方法时候,PC寄存器的值为Undefined(空)。该区域是java虚拟机规范中唯一不存在内存溢出异常的情况的区域。

2.java虚拟机栈,由一系列帧组成。因此又叫栈帧。它和程序计数器一样,也是线程私有的。它的生命周期和线程相同。描述的是java方法执行的内存模型。

每个方法在执行的同时都会创建一个帧,并压栈。用来存储局部变量表、操作数表、常量池指针、动态链接、方法出口等信息。而方法的调用到完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

局部变量表

局部变量表存放了编译器可知的各种基本类型的信息以及对象引用。其中每个槽位,也就是每个局部变量表空间Slot只能存储32位字节。因此。除了long和double类型的数据占用两个Slot之外。其他的

类型只占用一个。

局部变量表所需的内存空间在编译期间完成分配。因此,当一个方法调用时,该方法需要在帧中分配的局部变量表的大小也就确定了。在方法运行期间不会改变该方法局部变量表的大小。

操作数栈 :  由于java中没有寄存器,因此所有的参数传递都需要通过操作数栈。。

java栈上分配:不用垃圾回收器去回收,而是方法调用完毕之后自动回收。

—小对象,一般几十个byte,因为栈空间一般不会很大

—— 不是逃逸对象,也就是说这个对象只是属于我这个线程私有。如果除了本线程还有其他线程在使用它,那么就不能用栈上分配。因为栈上分配方法调用完立刻回收。

在java虚拟机规范中,java虚拟机栈这个区域可能出现两种异常。如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。如果线程请求的内存超过了栈空间的内存。此时会抛出OOM异常。

3.本地方法栈:主要为Native方法服务。在jvm规范中没有指定本地方法栈的语言、处理方式和数据结构。因此不详谈。

4.java堆(heap):所有线程共享的内存空间。基本所有new出来的对象都会存储在堆中。注意,是基本。

目前堆中使用的GC算法基本都是分代收集算法。因此堆也是分代的。可以细分为新生代、老生代。


根据java虚拟机规范:java堆可以位于物理上不连续的内存空间中。只要逻辑上是连续的就好。就像我们的磁盘空间的一样。在实现时,可以创建为固定大小的。

也可以创建为可扩展的。目前虚拟机中的堆内存一般都是可扩展的居多。可以用过-Xmx 和 -Xms 进行控制。

如果堆中没有内存进行实例的分配,而且堆也无法再度扩展时。会抛出OOF异常。内存溢出。

5.方法区:和堆一样,是各个线程共享的内存区域。用来存储已被虚拟机加载的类信息、常量、静态常量。也就是.class文件。

对于HotSpot虚拟机而言,方法区经常被称为永久代Permanent Generation。因为Hotspot虚拟机的设计团队选择把GC分代收集拓展到方法区中。因此GC可以像管理堆一样去

管理方法区。而不用为方法区区编写新的内存管理代码。但是对于其他的虚拟机而言。他们并没有永久代的概念。

 

原文地址:https://www.cnblogs.com/exceptionblog/p/8366152.html