8人参与 • 2026-04-30 • 内存
jvm运行时数据区域 也叫做:jvm内存布局。
jvm内存布局 和 java内存模型完全不同,jvm内存布局由5大部分组成。

1.堆区(线程共享):程序中所有创建的对象都保存在堆中。jvm最大空间。
2.栈(线程私有):
3.程序计数器(线程私有):保存下一条要执行的指令的地址。这里不是cpu寄存器储存的,而是内存空间,指令是java的字节码。不是二进制机器语言。
4.元数据区:(以前叫方法区)保存java代码中涉及类的相关信息。类的static属性。
在一个java进程中,元数据区 和 堆 是只有一份的。即同一个进程中的所有线程共用一份数据。
一个java进程中有多个线程,多个线程都有自己的 程序计数器 和 栈。所以每个线程都需要保存自己的“程序计数器” 每个线程都需要记录自己的 调用关系。

检测一下是掌握的怎么样,下面代码中的变量都储存在哪些区域?
public class test4 {
static class test{
private int a;
private test b = new test();
private static int c;
private static test d = new test();
}
public static void main(string[] args) {
int e = 10;
test f = new test();
}
}
一般地:
一个类的生命周期大致为下几个过程。

编写一个java程序 会得到一个 .java文件,在经过javac 编译 就会得到一个 .class文件。
想要运行java 进程,jvm就需要 读取 .class 文件并执行里面的指令。
具体步骤:
1.加载:把 .class 文件找到,然后打开并读取文件的内容。
代码中先见到 类的名字,然后进一步找到对应的 .class 文件(涉及一系列的目录查找过程)
2.验证:验证读到的 .class 文件的数据是否正确,是否合法。
java标准文档中明确规定 .class 文件的格式是怎么样的。
3.准备:分配内存空间。
根据读取到的内容大小,确定出类对象需要的内存空间,申请这样大小的空间,并把这个空间全部初始化为0.
4.解析:主要针对类中的字符串常量进行处理
java虚拟机 将字符串常量池中的 符号引用 替换为直接引用的过程,也就是初始化常量。
符号引用 其实就是指(字符串常量已经在 .class文件中)文件中 符号的位置,就是偏移量。
直接引用 就是直接保存变量的地址。

5.初始化:针对 类对象 做最终的初始化操作,执行静态成员赋值语句。

操作: 输入一个 类的全限定名(类似于java.lang.string),的到对应的 .class 文件。
属于 jvm加载类中的第一个机制:加载

为什么会是这个流程?
核心目的是 方式用户自己写的类把 标准库 或者 扩展库给覆盖掉。
保证 标准库的类是第一位,扩展库的类的是第二位。最后是第三方库的类。
防止程序员 不小心创建一个 和 系统中已有的类重名的类。导致加载的时候覆盖掉了系统的类。
gc 主要存在哪里呢?
栈 和 程序计数器 主要都是跟随线程的结束而结束。
元数据区:类对象涉及到的类加载,一个程序里面吗要加载得类都是有上限的,不会出现无限增长的情况。
所以 堆是gc得主要战区。
垃圾回收,回收内存 都是一对象为维度进行回收的。

gc回收的流程:
给每个对象分配一个计数器,衡量有多少个引用指向。

此时 对象中的计数器为0,就视为垃圾,需要回收。
上述方案存在两个问题:
1.消耗额外空间去 储存计数器
假设tets类,只有一个int(4字节)成员,那么就要花2个字节储存计数器,内存多用了 50%
2.引用计数可能会导致“循环引用”,使得上述判定出错。

这种情况就是最后 这俩对象计数器都不是0,都不能被释放。
用时间换取空间。
jvm中专门搞了一波周期性的线程,扫面代码中的所有对象,判定某个对象是否“可达”。
对应的,不可达的就是垃圾。
1.标记-清除法
如果直接对内存空间进行标记清楚,就有可能导致碎片化的空间无法充分利用。

这样剩下的三个空间就不容易利用了,碎片化的空间不能进行申请连续的空间。
2.复制算法
将被回收后的空间的一分为二,把不是垃圾的对象拷贝到另一侧。确保回收后得到连续的空间

这个算法缺点很明显:
1.内存空间利用率低
2.如果存活下来的对象比较多,复制成本也很大。
3.标记-整理
与标记-清楚类似,但是不一样的是后续并不是直接回收对象,而是让所有的存活对象都向前移动,最后直接清理掉边界标记的就可以了。
4.jvm中真正的解决方案。
如果获得年龄,使用可达性分析 对对象进行扫描,每次描扫后对象的年龄就+1

1.伊甸区比较大,让新创建的对象存放,大多数新创建的对象都活不过第一轮gc,留下来的对象拷贝到幸存区。
2.幸存区,是两个相按照复制算法将存活久对象复制到幸存区,反复多次。幸存区里也会为了保留完整空间进行左右多次复制。
3.一个对象在幸存区多次被拷贝,年龄不断增长,就要拷贝到老年代了。
4.根据经验规律,老年代的对象生命周期都比较长,即便如此也是要进行可达性分析的。
但是老年代的gc频率较低。老年代也需要通过标记整理。
分代回收时jvm的gc基本思想方法。
jvm还提供许多“垃圾回收器”对上述的分代回收 作进一步的扩充和具体实现。

cms涉及理念,把整个gc过程拆分成多个阶段,能和业务线程并发运行的。就尽量并发减少stw时间。

g1把整个内存分成很多快,不同的颜色表示这个一块区域是哪一块(新生代,老年代,幸存区...)
进行gc时不要求一周期就把多个内存都回收,只要回收一部分就好。限制一轮gc的工作量,目的是使stw控制在一定范围,降低stw的影响。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论