论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
IT评测·应用市场-qidao123.com技术社区
»
论坛
›
物联网
›
物联网
›
JVM 内存结构?
JVM 内存结构?
万有斥力
论坛元老
|
2024-9-23 19:07:41
|
显示全部楼层
|
阅读模式
楼主
主题
1668
|
帖子
1668
|
积分
5004
JVM 内存结构
这里的JVM内存结构,是指
Runtime Data Areas(运行时数据区)
。包含:
方法区(Method Area)
虚拟机栈(VM Stack)
本地方法栈(Native Method)
堆(Heap)
程序计数器(Program Counter Register)
各部分是否私有
通过上图可以知道:
线程私有的部分:
程序计数器
、
本地方法栈
、
虚拟机栈
;
线程间共享的部分:
堆
、
方法区
;
程序计数器
注意:这里的
程序计数寄存器
与
物理中的****CPU的寄存器
是不一样的。
JVM中的PC寄存器是对物理PC寄存器的一种抽象模仿。
程序计数器(Program Counter Register)
是一块较小的内存空间,它可以看作是当前线程所实验的字节码的
行号指示器
。 它是
线程私有的
。
工作细节:
通过改变这个计数器的值来选取下一条必要实验的字节码指令
,
程序控制流,分支、循环、跳转、异常处置惩罚、线程规复等底子功能都必要依靠这个计数器来完成
。
虚拟机栈
虚拟机栈(Java Stack)
,也叫
Java栈
,就是我们平常说的
栈
。
线程私有的
。
除了Native 方法(它通过本地方法栈实现),
其他的 Java 方法调用都是通过栈来实现的
(也必要和其他运行时数据区域好比程序计数器共同)。
每个方法被实验的时间,Java虚拟机都会同步创建一个
栈帧(Stack Frame)
用于
存储局部变量表、利用数栈、动态连接、方法出口等信息
。
局部变量表
:存放局部变量的,以**局部变量槽(Slot)**来表现。
局部变量表的巨细是确定的
,在编译期间完成分配的。
利用数栈
:重要作为方法调用的中转站利用,
用于存放方法实验过程中产生的中间计算结果
。别的,
计算过程中产生的临时变量也会放在利用数栈中
。
动态连接
:用于 一个方法必要调用其他方法的场景。ava 源文件被编译成字节码文件时,变量、方法的引用都是 符号引用(生存在Class 文件的常量池里)。
动态链接 就是在方法调用其他方法时,将 符号引用转换为 直接引用的
。
问:垃圾接纳是否涉及栈内存?
答:不会,由于栈的运行结束后会按从顶至底的序次移除栈对应的线程的方法,所以不必要垃圾接纳机制来处置惩罚长久不用的垃圾。
问:栈内存分配越大越好吗?
答:不是的,我们的物理内存是有限的,如果栈内存分配过大,会导致我们能运行的线程数变少。
可以通过-Xss size(分配的巨细)来指定栈内存的巨细。
问:方法内的 局部变量 是线程安全的吗?
分析:如果它是线程私有的,那肯定是线程安全的。否则就是线程县城不安全的。
答:分环境:
如果这个方法中的局部变量不是static修饰。那就是线程安全的。
①如果这个方法中的局部变量是static修饰。那就是线程不安全的。②如果传入的参数是一个可变对象,并且这个对象在多个线程中被共享和修改,那就可能出现线程安全问题。
本地方法栈
本地方法栈(Native Method Stacks)
与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机 栈为虚拟机实验 Java方法(也就是字节码)服务,而
本地方法栈则是为虚拟机利用到的本地(Native) 方法服务
。
一些带有native
关键字
的方法就是必要JAVA去调用本地的C大概C++方法。
堆
Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建
。此内存区域的唯一目的就是
存放对象实例
,Java 世界里“险些”所有的对象实例都在这里分配内存。
但也不绝对,从 JDK 1.7 开始已经默认开启
逃逸分析
,如果某些方法中的
对象引用没有被返回
大概
未被外面利用(也就是未逃逸出去)
,
那么对象可以直接在栈上分配内存
。
Java 堆区域可细分为:
JDK7 及之前分为:新生代内存(Young Generation)、老生代(Old Generation)、
永久代(Permanent Generation)
;
JDK8 及之后:新生代内存(Young Generation)、老生代(Old Generation);
Metaspace(元空间)
(元空间:利用的是
本地内存
,它只是替换了 永久代 的作用,但**它不属于 堆内存,而是存放于本地内存**)
1、新生代内存(Young Generation):
作用:用于存放
新创建的对象
。大部分对象通常具有
短暂的生命周期
,因此在新生代中频繁进行垃圾接纳,可以高效地接纳大量短生命周期的对象,减少内存占用。
GC接纳:新对象会先被分配到Eden区。当Eden区满了之后触发Minor GC(新生代垃圾接纳)。在接纳过程中,存活下来的对象会被移到其中一个Survivor区;当Survivor区也满了,对象会进一步提拔到老年代。
构成:由年轻区(Eden区)、两个Survivor区(from survivor和to survivor)。默认环境下,新生代的Eden区和Survivor区的空间巨细比例是8:2,可以通过-XX:SurvivorRatio参数调整。
2、老年代:存放颠末多次垃圾接纳仍然存活的对象,大概是大对象(某些JVM可以直接在老年代分配大对象)。老年代中的对象被认为是长时间存活的,因此接纳的频率较低。
大对象:通常指的是必要占用较多连续内存空间的Java对象实例
3、永久代、元空间:(二者作用一样,只是元空间 替换 永久代)
作用:它是具体实现的
方法区
:用来存放类的元数据信息,包括类的结构信息、常量池、静态变量、即时编译后的代码等。
为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?
重要考虑的是,永久代利用的是JVM内存,因此,当存放的数据太多时,很轻易导致永久代内存溢出(PermGen OutOfMemoryError)。
而元空间利用本地内存而不是 JVM 堆内存,理论上只受限于利用系统的内存巨细。这大大减少了由于类信息、常量等存储导致内存溢出的风险。
方法区
方法区(Method Area)是
线程共享
的。它是Java 虚拟机规范的一个逻辑部分,别名
非堆(Non-Heap)
,目的是与 Java的 堆(Heap)区分开。在不同的虚拟机实现上,方法区的实现是不同的。
方法区重要用于存储已被虚拟机加载的
类信息、常量、静态变量、即时编译器编译后的代码、运行时常量池
。虚拟机启动时就会创建方法区。
JDK中方法区的不同实现
以HotSpot虚拟机来讲,在JDK不同版本汇总,重要关注
字符串常量池位置变迁
:
Jdk1.6及之前: 有永久代,
运行时常量池在永久代,运行时常量池包含字符串常量池
;
Jdk1.7:有永久代,但已经逐步“去永久代”,
字符串常量池从永久代里的运行时常量池分离到堆里
;
Jdk1.8及之后: 无永久代,
运行时常量池在元空间,字符串常量池里依然在堆里
。
问:为什么要将字符串常量池移动到堆中?
答:重要是由于永久代(方法区实现)的 GC 接纳服从太低,只有在整堆收集 (Full GC)的时间才会被实验 GC。Java 程序中通常会有大量的被创建的字符串等待接纳,
将字符串常量池放到堆中,可以或许更高效实时地接纳字符串内存
。
String 类的 intern()方法
参考:https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
调用字符串对象的intern方法,会将该字符串对象实验放入到
字符串常量池
中:
如果
字符串常量池
中没有该字符串对象,会将该字符串对象复制一份,再放入到
字符串常量池
中;
如果有该字符串对象,则放入失败;
知道了intern()方法的作用,下面来看看String中创建字符串时的区别(以下面的代码示例):
要知道:
直接利用双引号声明出来的String对象会直接存储在字符串常量池中。如:String a = "jack";
利用new创建的String对象,则会在 字符串常量池中创建字符串,并在堆中创建String对象。
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
}
复制代码
JDK1.6 中输出 false、false
由于,JDK1.6中,
字符串常量池
位于 堆的
永久代区域
(我们知道 JVM 堆中的 永久代 是是具体实现的
方法区
),而上图中的 堆(Heap)是特指新生代、老年代。所以:
String s = new String("1");:会在 字符串常量池 中创建字符串"1”,然后在JAVA 堆中创建String对象obj(存储的是 字符串常量池“1”的引用),然后栈中的s指向Java heap中的 obj。
String s2 = "1";是栈中的s2直接去 字符串常量池 中查找是否有“1”,有则直接指向。无则在字符串常量池中创建,然后再指向。
jdk1.7 中输出 false、true
jdk1.7中,字符串常量池 从永久代 移动到正常的 Java 堆(中的 年轻代、老年代)中了。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
正序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
发新帖
回复
万有斥力
论坛元老
这个人很懒什么都没写!
楼主热帖
MyBatis-Plus入门教程及基本API使用案 ...
几个函数的使用例子:更新VBRK-XBLNR, ...
EFCore 动态拼接查询条件(表达式树方式 ...
OpenJDK和OracleJDK的区别说明
深度理解 C# 中的 for 和 foreach ...
阿里巴巴Java开发手册(全册四版) ...
Excel 制作可视化看板的思路及操作 ...
.net 发邮件的小工具,包含json,环境 ...
2022年混过的那些SAP项目
跨域攻击的方法介绍
标签云
集成商
AI
运维
CIO
存储
服务器
登录参与点评抽奖加入IT实名职场社区
下次自动登录
忘记密码?点此找回!
登陆
新用户注册
用其它账号登录:
关闭
快速回复
返回顶部
返回列表