JVM从浅入深之概述2020-3-8

话说天下武功唯快不破,为什么java一经编译,随处运行(跨平台性),这就是他最大的优势,这就是因为自动的内存管理,内存的自动分配和释放),接下来,详细介绍一下:

一Java内存模型的概述(JDK1.7):
在
线程共享数据区:方法区 ,堆
线程隔离数据区(私有):Java虚拟机栈,本地方法栈,程序计时器

JDK1.8的运行时内存划分:
在这里插入图片描述
可以看出JDK1.8的时候,变化:
1、方法区从JVM中取出。
2、方法区移入到本地内存,更名为元数据区。(方法区也称永久代,主要是因为永久代和元数据区都是方法区的一种实现)。
3、在JDK1.7之前,字符串常量池是存在于方法区内的,JDK1.7之后字符串常量池就从方法区内取出,存放在堆空间里面了。
JDK1.8变更的原因:
字符串在永久代中,容易出现性能问题和内存溢出;
类及方法和信息等比较难确定其大小,因此对于永久代的代销指定比较困难,太小容易出现永久代溢出,太大容易出现老年代溢出;
永久代会为GC带来不必要的复杂度,并且回收效率偏低;
Oracle可能会将HotSpot 与JRockit 合二为一
**方法区:**方法区中存储了每一个类的结构信息,如运行时常量池字段、方法数据、构造函数、普通方法的字节码内容、类、实例、接口初始化时需要用到的特殊方法等数据;但是在HotSpot中,方法区只是逻辑上的独立,实际上方法区还是包含在堆内存中的,也就是说,方法区在物理上还是属于Heap的。因此方法区也是在JVM启动时被创建的,但大多数人会把它称为永久代(Permanent Generation),主要原因有两点
可以通过选项:"-XX:MaxPermSize" 指令,可以设置内存大小进行动态扩展不会像Heap中一样频繁的被GC回收,甚至可以显示指定是否需要在程序运行时回收方法区中的数据。不显示指定时,GC的回收目标仅针对方法区中的常量池和内存卸载。方法区的内存一旦超过 “-XX:MaxPermSize” 所指定的内存大小,就会抛出 “OutOfMemoryError” 异常。
**堆:**是Java虚拟机所管理的内存中最大的一块,Java堆区是在JVM启动时创建的,它在实际的内存空间中可以是不连续的;Heap是一块用于存储对象实例的内存区;存储在Heap的对象可以分为两类:瞬时对象,生命周期较短;另一类则是生命周期较长;对于生命周期不同的JAVA对象,应该采用不同的垃圾收集策略,因此产生了分代垃圾回收策略。可以通过在启动时自定义分配Heap的大小,-Xmx 和 -Xms,-Xmx:表示Heap的起始内存,-Xms:表示Heap的最大内存,若Heap中的内存超过 -Xmx 指定的 -Xms 时,就会抛出 “OutOfMemoryError” 异常
**Java虚拟机栈:**虚拟机栈是由一个一个栈帧组成的,栈帧是在每个方法被调用时产生的。每个栈帧又由局部变量区,操作数栈等组成。每当方法开始运行时,则创建一个栈帧压栈,当一个方法执行完毕后,则出栈。这里举个比较著名的 stackoverflow 异常,当程序中出现方法递归调用且没有结束语句,抑或是死循环调用,就会抛此异常。
**本地方法栈:**在《深入理解JAVA虚拟机》中有这么一句话:“Java虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务。”对于这句话的,我的理解是:Java虚拟机不仅使用Java方法,也使用其他语言的方法(C、C++…),比如CAS操作中的UNSAFE类:提供了硬件级别的原子操作,而这个类里面的具体方法使用native关键字修饰,表明这是一个其他语言编写的方法,这些方法是要放到本地方法栈里面运行的。
**程序计时器**:程序计数器被用来记录当前线程正在执行的字节码行号,这样 JVM 可以知道下一条该执行哪一行字节码。当多线程运行时,CPU 切换线程,可以通过程序计数器知道上一次该线程执行到哪里,什么状态。以便接着运行下去。
元数据区(共享):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池:运行时常量池是方法区的一部分,其中存放了一些符号的引用。当我们 new 一个对象时,会检查这个区域是否已经有该对象的引用。

原创文章 4 获赞 11 访问量 591

猜你喜欢

转载自blog.csdn.net/qq_33570145/article/details/104740990