【jvm系列-04】精通运行时数据区共享区域---堆_java 数据共享区

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
img

正文

	- [5.2,TLAB](#52TLAB_286)
	- * [5.2.1,为什么要有TLAB](#521TLAB_290)
		* [5.2.2,什么是TLAB](#522TLAB_296)
+ [6,堆空间参数设置](#6_310)
+ [7,逃逸分析(重点)](#7_329)
+ - [7.1,栈上分配](#71_333)
	- [7.2,同步省略](#72_363)
	- [7.3,标量替换](#73_377)
+ [8,堆空间是分配对象的唯一选择吗(重点)](#8_398)

一,深入理解运行时数据区堆

1,堆空间概念

heap 堆属于是运行时数据区的一块空间,属于一块比较重要的一部分,并且该区域的数据属于线程共享。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUkV3HXx-1679902148714)(img/1678336702138.png)]

⚽ 一个JVM实例只存在一个堆,堆也是java内存管理的核心区域

⚽ Java堆在启动的时候被创建,其空间大小也被确定,并且是JVM管理的最大的一块内存空间(可调节)

⚽ 堆可以处于物理上不连续的内存空间中,但是在逻辑上他应该是被视为连续的

⚽ 所有的线程共享JVM堆,在这里还可以划分为私有的缓冲区(Thread Local Allocation Buffer)

⚽ 在java虚拟机规范中,所有的对象实例以及数组都应该分配在堆上

⚽ 数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置

⚽ 在方法结束之后,堆中的对象不会被立马移除,仅仅在垃圾收集的时候才会被移除

⚽ 堆是GC(Garbage Collection,垃圾回收器)执行垃圾回收的重点区域

2,堆内存空间

2.1,堆内存的细分

在JDK8之前,堆内存的逻辑主要分为:新生代+老年代+永久代

在JDK8以及JDK8之后,堆内存主要分为:新生代+老年代+元空间

jdk8的垃圾回收器如下,可以直接在jdk安装目录下,找到bin目录,然后打开这个 jvisaulvm.exe 文件,然后工具的在插件中安装一个 Visaul GC,就可以看到下面的界面,可知在jdk8中主要是分为Eden区,old区和Metaspace这三个区域,分别对应着新生代,老年代和元空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2jtndB60-1679902148716)(img/1679559106674.png)]

也可以直接新建一个application运行并设置其对应jvm的参数,如设置初始大小和最大大小为10m,并在控制台上将这个堆信息打印出来

-Xms10m -Xmx10m -XX:+PrintGCDetails

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jfjgLVT6-1679902148717)(img/1679559765878.png)]

在运行项目后,可以发现,PSYoungGen新生代,ParOldGen老年代,Metaspace元空间,其老年代所分配的内存最大。

hello
Heap
PSYoungGen total 2560 K, used 1086 K[0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048 K, 28 % used[0x00000000ffd00000, 0x00000000ffd91b68, 0x00000000fff00000)
from space 512 K, 98 % used[0x00000000fff00000, 0x00000000fff7e030, 0x00000000fff80000)
to space 512 K, 0 % used[0x00000000fff80000, 0x00000000fff80000, 0x0000000100000000)
ParOldGen total 7168 K, used 371 K[0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168 K, 5 % used[0x00000000ff600000, 0x00000000ff65cf38, 0x00000000ffd00000)
Metaspace used 3520 K, capacity 4498 K, committed 4864 K, reserved 1056768 K
class space used 388 K, capacity 390 K, committed 512 K, reserved 1048576 K

2.2,堆大小的基本设置

Java堆区主要用于存储Java对象的实例,那么堆的大小在JVM启动的时候就已经设定好了,可以直接通过 -Xmx和 -Xms来设定堆的初始内存和最大内存,当堆的最大内存超过设置的 -Xms 最大内存时,则会直接抛出内存溢出异常。

-Xms : 设置堆空间(新生代+老年代)的初始内存大小  
	   -X表示的是Jvm的运行参数
 ​	   ms表示的是memory start
 -Xmx:设置堆空间(新生代+老年代)的最大内存大小 

并且在默认的堆空间中,其最大内存为物理电脑内存 / 4,初始内存为物理电脑内存 / 64

在实际开发中,更加建议将初始堆内存和最大的堆内存设置成相同的值,主要是省去堆空间频繁的扩容和释放带来的压力,从而增加这种资源的利用率。

2.3,堆大小的计算规则

堆大小主要分为新生代区和老年代区,新生代又分为eden区survive存活区,survive区有分为survive0和survive1区,对象只能存在期中的一个区域里面,每Minitor GC一次,就会从一个survive区到另外一个survive区,如果没有被清理掉,那么他的年龄就会+1,当年龄达到15时,就会从新生代中加入到老年代里面。

接下来在虚拟机中设置一个600m的堆大小,依旧是用刚刚那个程序

-Xms600m -Xmx600m

然后在main方法中运行之后,输入以下命令

jps : 查看进程号
jstat -gc 进程号

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JK6wDrO-1679902148718)(img/1679562666231.png)]

可以发现新生代有S0和S1的survive区和Eden区,老年代有old区。对应的容量分别是最大容量和已使用容量,如S0C表示的是survive0区的最大容量,S0U则表示的是survice0已使用的容量。后面也有对应的YGC和FGC发生的次数。

并且在计算总容量的时候,因为数据只能存在一个survive中,因此只计算一个survice的内存大小,所以有时计算出来的内容是小于实际设置的内容,但结果是对的,只是jvm在统计时只统计了一个survice存活区的大小。

3,堆对象

3.1,堆空间对象组成

在JVM中,java对象可以分为两类

一类是生命周期较短的瞬时对象,这类对象的创建和消亡都比较快

另一类周期长,某些极端情况下可以和JVM进程的生命周期保持一致

java堆细分可以分为新生代(YoungGen)和老年代(OldGen),年轻代又可以细分为Eden区和Survivor0空间和Survivor1空间,有时也被称为(from区和to区)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uisYKQWJ-1679902148719)(img/1679621815639.png)]

在这些对象中,也会对新生代和老年代的空间大小设置对应的比例,新生代和老年代的比例默认为1:2

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-js0F3Bii-1679902148719)(img/1679623061927.png)]

当然也可以通过命令进行修改这个默认的比例,一般不会修改这个默认值,除非是知道这个老年代的对象偏多

-XX:NewRatio=2 : 表示新生代占1,老年代占2,新生代占1/3,默认比例
-XX:NewRatio=4 : 表示新生代占1,老年代占4,新生代占1/5

也可以直接通过命令查看,通过jps查出进程号之后,通过Jinfo去查看

jps
jinfo -flag NewRatio process(进程号)

除了老年代和新生代的对空间有比例之外,这个新生代中的Eden区和Survicor区也有对应的比例。如果是在Linux系统下,其比例为8:1 ;如果是在Windows系统下,其比例为6:1

-XX:SurvivorRatio=8 : 表示eden区占8,Survicor区占1
-XX:SurvivorRatio=6 : 表示eden区占6,Survicor区占1
-XX:SurvivorRatio=4 : 表示eden区占4,Survicor区占1

这个比例也可以直接通过命令查看,通过jps查出进程号之后,通过Jinfo去查看

jps
jinfo -flag SurvivorRatio process(进程号)

因此可以得到一下结论

🎱 几乎所有的Java对象都是在Eden区被new出来

🎱 绝大多数对象的销毁在新生代就进行了

3.2,堆对象分配的一般过程

为新对象分配内存时一件非常严谨和复杂的任务,JVM的设计者们不仅需要考虑内存如何分配,在哪里分配等问题,并且由于内存分配算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收后是否会在内存空间中产生内存碎片等问题。

🎱 在new一个对象时,会先将对象放在eden区,并且该去有大小限制

🎱 当eden区的数据量满了,程序还需要创建对象的时候,JVM的Minor Gc就会对Eden区的垃圾进行回收,当Eden区不再被其他对象引用的对象就进行销毁,被引用的对象就加载到Survivor0区中,然后此时eden区为空,再将新数据加载到eden区中

🎱 只有eden去满才会触发这个Minor GC,但是在触发这个垃圾回收时,不仅会回收Eden区的对象,也会回收这个Survivor区的对象

🎱 如果再次触发垃圾回收,就会将eden区中的未被引用的对象销毁,并且此时会检查survicor0区中的的数据,如果不存在有对该对象的引用,那么该对象也会被销毁,然后将eden区未被销毁的和survicor0未被销毁的一起加入到survicor1区,后面这两个区的对象往返移动

🎱 每将对象移动一次survicor区,其age年龄就会加1,初始值为1,阈值为默认为15,可以修改,当年龄达到阈值时,其再gc一次到达16,就会将对象加入到老年代的空间区域中

🎱 当老年代中的空间满了时,就会触发这个Full GC

总结

  • 针对幸存者s0和s1的总结:复制之后有交换,谁不空谁是from,谁空谁是to
  • 针对垃圾回收总结:频繁回收在新生区,很少在老年区回收,几乎不在永久区/元空间回收
3.3,堆对象分配的特殊过程

🌲 前两个步骤还是一样,在new一个对象时,会先将对象放在eden区,并且该去有大小限制

🌲 当eden区的数据量满了,程序还需要创建对象的时候,JVM的Minor Gc就会对Eden区的垃圾进行回收,当Eden区不再被其他对象引用的对象就进行销毁,被引用的对象就加载到Survivor0区中,然后此时eden区为空,再将新数据加载到eden区中

🌲 这里开始就不一样了,如果遇到的是一个大对象,可以在eden区存的下,但是在survivor区存活不下,那么直接将这个对象晋升为老年代

🌲 如果遇到的是一个超大对象,在eden区放不下,那么也会直接晋升为老年代,将这个超大对象存入到老年区中,如果老年代内存不够,那么就会触发Full GC,如果还是不够或者内存大小直接小于这个超大对象的大小,那么就会直接触发这个OOM,内存溢出错误。

3.4,代码示例

给对应的jvm堆配置的大小如下:-Xms600m -Xmx600m ,然后其对应的代码如下,就是写一个死循环,一直去创建Student对象,list作为一个对象的引用,只要list存在,里面的对象就不会被回收掉。

public class Student {
    //创建bu数组
    byte bu[] = new byte[new Random().nextInt(1024\*200)];
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        while (true){
            list.add(new Student());
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

接下来继续通过这个jvisualvm这个jdk自带的工具来查看堆内信息,一段时间之后,就发现出现了这个OOM。

如下图所示,Old区占400内存,年轻代占200内存,所以可知这个老年代区域:新生代区域为2:1;eden区大小为150,survicor为25,我这个是在windows系统下,因此也符合6:1,如果是linux系统,那就是8:1了。

而这个eden区到达峰值时,就表示eden区的数据满了,那么就会触发一次Minor GC,那么就会有数据存储在survivor中,再到达一次峰值,又会触发一次Minor GC,因此可以看到数据在Survivor的两个区中移动,而由于对象一直被引用着,那么不管是Full GC还是Minor GC都是不能将数据给移除的,因此这个young区的数据不能被清除,数据就会都存在old区中,那么old区的数据就是成直线上升的,当old区满了之后,那么就会直接触发这个OOM了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gVI5PqQP-1679902148720)(img/1679639870705.png)]

4,初识GC

4.1,初识Minor GC,Major GC,Full GC

JVM在进行GC的时候,并非每次都对上面三个内存(新生代,老年代和方法区)区域一起回收,大部分的时候指的还是新生代。

而在HotSpot Vm中,GC又分为两种类型:部分收集和整堆收集

  • 部分收集,不是完整的收集整个Java堆的垃圾收集,期中又分为:

新生代收集(Minor GC / Young GC):只是新生代的垃圾收集

老年代收集(Major GC / Old GC):老年代的收集器,很多时候Major GC和Full GC会混合使用,区要区分是整堆回收还是老年代回收

混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集

  • 整堆收集(Full GC),收集整个java堆和方法区的垃圾收集
4.1.1,新生代GC的触发条件

🐣 当新生代空间不足时,就会触发Minor GC,这里的新生代指的是Eden区,Survivor区是不会触发GC的

🐣 Java对象大多具有朝生夕灭的特性,所以Minor GC也非常频繁,回收速度也比较快

🐣 Minor GC会触发STW,就是暂停其他的用户线程,等垃圾回收结束,才会恢复运行

4.1.2,老年代GC的触发条件

🐣 指发生在老年代的GC,对象从老年代消失,即 Major GC或者Full GC 发生了

🐣 出现了Major GC之后,经常会伴随着至少一次的Minor GC

🐣 Major GC的速度一般会比Minor GC慢10倍,其STW的时间甚至要更长

🐣 如果Major GC 之后,内存还是不足,就会直接报OOM了

4.1.3,Full GC的触发机制

🐣 调用System.gc()时,系统建议执行Full GC时,但是不必然执行

🐣 老年代或者方法区空间不足

🐣 Minor GC进入老年代的平均大小大于老年代的可用内存

🐣 直接加入到老年代的对象的大小大于老年代可用内存的大小

5,堆内存分配

5.1,基本的内存分配策略

🐋 将对象优先分配到Eden区

🐋 大对象直接分配到老年代,尽量避免程序中出现过多的大对象,如大树组,字符串等

🐋 长期存活的对象分配到老年代

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

🐣 直接加入到老年代的对象的大小大于老年代可用内存的大小

5,堆内存分配

5.1,基本的内存分配策略

🐋 将对象优先分配到Eden区

🐋 大对象直接分配到老年代,尽量避免程序中出现过多的大对象,如大树组,字符串等

🐋 长期存活的对象分配到老年代

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
[外链图片转存中…(img-I7QPh9Ah-1713345465552)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JVM内存模型是Java虚拟机在运行所使用的内存分配和管理方式。它包括了运行数据,也就是JVM在内存中划分的不同区域,用来存储程序的数据和指令。 JVM运行数据主要包括以下几个部分: 1. 堆(Heap):用于存储对象实例和数组。堆是线程共享区域,所有线程共同使用堆来创建和访问对象。 2. 方法(Method Area):用于存储已加载的类信息、常量、静态变量和编译后的代码等。方法也是线程共享区域,它在内存中占用一块连续的空间。 3. 虚拟机栈(VM Stack):每个线程在创建都会分配一个虚拟机栈,用来存储局部变量和方法调用信息。虚拟机栈是线程私有的,每个线程都有自己独立的虚拟机栈。 4. 本地方法栈(Native Method Stack):与虚拟机栈类似,用于存储本地方法调用的相关信息。 5. 程序计数器(Program Counter Register):用于存储当前线程执行的字节码指令的地址。 这些不同的运行数据JVM内存模型中起着不同的作用,可以提供给程序运行所需的各种资源和环境。例如,堆用于存储对象实例,方法用于存储类信息和静态变量,虚拟机栈用于存储方法的局部变量和方法调用信息等。 总的来说,JVM内存模型和运行数据是Java虚拟机在运行所使用的内存管理和分配方式。它们的不同区域有不同的作用,用来存储程序的数据和指令。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [运行数据JVM内存模型](https://blog.csdn.net/weixin_45659364/article/details/124027073)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [JVM:Java内存模型与运行数据区域](https://blog.csdn.net/m0_71777195/article/details/131655107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java内存模型与JVM运行数据别详解](https://download.csdn.net/download/weixin_38648037/12745990)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值