IT评测·应用市场-qidao123.com技术社区

标题: JVM 内存结构? [打印本页]

作者: 万有斥力    时间: 2024-9-23 19:07
标题: JVM 内存结构?
JVM 内存结构

这里的JVM内存结构,是指Runtime Data Areas(运行时数据区)。包含:


各部分是否私有

通过上图可以知道:
程序计数器

   注意:这里的 程序计数寄存器物理中的****CPU的寄存器 是不一样的。JVM中的PC寄存器是对物理PC寄存器的一种抽象模仿。
  程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所实验的字节码的行号指示器。 它是线程私有的
工作细节:通过改变这个计数器的值来选取下一条必要实验的字节码指令程序控制流,分支、循环、跳转、异常处置惩罚、线程规复等底子功能都必要依靠这个计数器来完成
虚拟机栈

虚拟机栈(Java Stack),也叫Java栈,就是我们平常说的线程私有的
除了Native 方法(它通过本地方法栈实现),其他的 Java 方法调用都是通过栈来实现的(也必要和其他运行时数据区域好比程序计数器共同)。
每个方法被实验的时间,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、利用数栈、动态连接、方法出口等信息

问:垃圾接纳是否涉及栈内存?

答:不会,由于栈的运行结束后会按从顶至底的序次移除栈对应的线程的方法,所以不必要垃圾接纳机制来处置惩罚长久不用的垃圾。
问:栈内存分配越大越好吗?

答:不是的,我们的物理内存是有限的,如果栈内存分配过大,会导致我们能运行的线程数变少。
可以通过-Xss size(分配的巨细)来指定栈内存的巨细。
问:方法内的 局部变量 是线程安全的吗?

分析:如果它是线程私有的,那肯定是线程安全的。否则就是线程县城不安全的。
答:分环境:
本地方法栈

本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机 栈为虚拟机实验 Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机利用到的本地(Native) 方法服务
   一些带有native关键字的方法就是必要JAVA去调用本地的C大概C++方法。
  

Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,Java 世界里“险些”所有的对象实例都在这里分配内存。
但也不绝对,从 JDK 1.7 开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回大概未被外面利用(也就是未逃逸出去)那么对象可以直接在栈上分配内存
Java 堆区域可细分为:



1、新生代内存(Young Generation):


2、老年代:存放颠末多次垃圾接纳仍然存活的对象,大概是大对象(某些JVM可以直接在老年代分配大对象)。老年代中的对象被认为是长时间存活的,因此接纳的频率较低。
   大对象:通常指的是必要占用较多连续内存空间的Java对象实例
  3、永久代、元空间:(二者作用一样,只是元空间 替换 永久代)

为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

重要考虑的是,永久代利用的是JVM内存,因此,当存放的数据太多时,很轻易导致永久代内存溢出(PermGen OutOfMemoryError)。
而元空间利用本地内存而不是 JVM 堆内存,理论上只受限于利用系统的内存巨细。这大大减少了由于类信息、常量等存储导致内存溢出的风险。
方法区

方法区(Method Area)是线程共享的。它是Java 虚拟机规范的一个逻辑部分,别名 非堆(Non-Heap),目的是与 Java的 堆(Heap)区分开。在不同的虚拟机实现上,方法区的实现是不同的。
方法区重要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码、运行时常量池。虚拟机启动时就会创建方法区。
JDK中方法区的不同实现

以HotSpot虚拟机来讲,在JDK不同版本汇总,重要关注字符串常量池位置变迁
问:为什么要将字符串常量池移动到堆中?

答:重要是由于永久代(方法区实现)的 GC 接纳服从太低,只有在整堆收集 (Full GC)的时间才会被实验 GC。Java 程序中通常会有大量的被创建的字符串等待接纳,将字符串常量池放到堆中,可以或许更高效实时地接纳字符串内存
String 类的 intern()方法

   参考:https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
  调用字符串对象的intern方法,会将该字符串对象实验放入到字符串常量池中:

知道了intern()方法的作用,下面来看看String中创建字符串时的区别(以下面的代码示例):
   要知道:
  
  1. public static void main(String[] args) {
  2.     String s = new String("1");
  3.     s.intern();
  4.     String s2 = "1";
  5.     System.out.println(s == s2);
  6.     String s3 = new String("1") + new String("1");
  7.     s3.intern();
  8.     String s4 = "11";
  9.     System.out.println(s3 == s4);
  10. }
复制代码
JDK1.6 中输出 false、false


由于,JDK1.6中,字符串常量池 位于 堆的永久代区域(我们知道 JVM 堆中的 永久代 是是具体实现的方法区),而上图中的 堆(Heap)是特指新生代、老年代。所以:
jdk1.7 中输出 false、true


jdk1.7中,字符串常量池 从永久代 移动到正常的 Java 堆(中的 年轻代、老年代)中了。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com技术社区 (https://dis.qidao123.com/) Powered by Discuz! X3.4