JVM堆详解, 了解不迷路

2023-11-02 20:20
文章标签 java jvm 详解 了解 迷路

本文主要是介绍JVM堆详解, 了解不迷路,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上周工作比较忙,又是上线, 又是加班的, 搞得学习进度拉下来很多。

上周也是有很大收获的, 自己上周, 学习到了一个牛逼的学习方法, 你通过我的学习笔记, 其实就可以看出来, 其中更多了很多自己的思考。

第一:论语士学习:我们通过目录结构, 去提问题, 和作者去对答案

第二:记忆曲线, 我觉得大家应该都知道, 那我们又如何打造自己的学习曲线, 整理自己学习到的东西不会忘记呢, 推荐一个神器, ankiapp, 通过程序化的推荐方式, 利用碎片话的时间, 帮助我们打造完成的知识体系

我们正在使用, 请大家跟我一起学习起来。 持续精进, 不断打造自己的知识体系。让自己越来越优秀。

1. JVM中的堆和栈有什么不同?

栈解决的程序运行问题,即程序如何执行,或者说如何处理数据;

堆解决的是数据存储问题,即就是数据如何放、放哪儿

2. 什么要把堆和栈分开, 这样设计有什么好处呢?

  1. 从设计角度, 堆存数据, 栈处理逻辑, 这样设计区分, 可以隔离和模块化, 分而治之的思想

  2. 共享内存(多线程访问一个对象),节省内存

  3. 栈的地址, 只能连续增加, 堆可以动态增加, 相互拆分, 栈关联堆的一个地址即可, 提高内存利用率

  4. 对象就是对债完美结合, 对象属性存储数据, 就是堆, 栈就是对象的方法

3. 堆中内存是如何划分的

1. 怎么区分老年代和年轻代

年轻代中的对象, 生命周期很短, 基本很快就死了, 也就是被GC了。

老年代中的对象, 都是一些老顽固, 经过15次回收的对象或者大对象才存到老年代中

从年轻代进入老年代

第一种的情况就是将躲过15次MinorGC的对象移动到老年代.

第二种就是动态年龄对象判断,既Survivor区的经历过两次GC的对象大小大于Survivor区容量的一半的时候,如Survivor区是100m,里面的对象之和大于50m.就将这些2次GC还存活的对象移入到老年代中去.

第三种就是大对象,还没有进入到新生代的时候就被移动到老年代,这里有个参数,为-XX:PretenureSizeThreshold,可以设置值,比如1m,那么再进入堆内存的时候,就会检查这个实例对象的大小,如果大于这个阈值,就直接进入到老年代.

2. 什么是Stop the World?

Full GC的时候, 就是Stop the World.

系统在进行垃圾回收的时候会暂停java程序的执行.直到垃圾回收完成.所谓jvm的迭代演进其实就是基本上就是在解决减少垃圾回收,和垃圾回收的时间尽可能的短.一起都取决于垃圾回收算法.

3. 堆内存中内存划分比例

老年代:年轻代 =  2:1

年轻代中:eden(伊甸区) :service0(幸存区0) :service1(幸存区1) = 8:1:1

jvm堆空间.jpg

4. 老年代和年轻代为什么是2:1

**默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 **

( 该值可以通过参数 –XX:NewRatio 来指定 )

5. 为什么会有survivorto?

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。你也许会问,执行时间长有什么坏处?频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。好,那我们来想想在没有Survivor的情况下,有没有什么解决办法,可以避免上述情况:

方案优点缺点
增加老年代空间更多存活对象才能填满老年代。降低Full GC频率随着老年代空间加大,一旦发生Full GC,执行所需要的时间更长
减少老年代空间Full GC所需时间减少老年代很快被存活对象填满,Full GC频率增加

显而易见,没有Survivor的话,上述两种解决方案都不能从根本上解决问题。

我们可以得到第一条结论:Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代。

6. 为什么要设置两个Servivor区

设置两个Survivor区最大的好处就是解决了碎片化

eden:survivorfrom:survivorto = 8:1:1

Java堆内存分为年轻代和老年代,其中, 年轻代分为Eden区和survival区,survival又分为fromSurvival和toSurvival。

为什么一个Survivor区不行?第一部分中,我们知道了必须设置Survivor区。假设现在只有一个survivor区,我们来模拟一下流程:刚刚新建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中的存活对象就会被移动到Survivor区。这样继续循环下去,下一次Eden满了的时候,问题来了,此时进行Minor GC,Eden和Survivor各有一些存活对象,如果此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化。我绘制了一幅图来表明这个过程。其中色块代表对象,白色框分别代表Eden区(大)和Survivor区(小)。Eden区理所当然大一些,否则新建对象很快就导致Eden区满,进而触发Minor GC,有悖于初衷。

15.jpg

碎片化带来的风险是极大的,严重影响JAVA程序的性能。堆空间被散布的对象占据不连续的内存,最直接的结果就是,堆中没有足够大的连续内存空间,接下去如果程序需要给一个内存需求很大的对象分配内存。。。画面太美不敢看。。。这就好比我们爬山的时候,背包里所有东西紧挨着放,最后就可能省出一块完整的空间放相机。如果每件行李之间隔一点空隙乱放,很可能最后就要一路把相机挂在脖子上了。那么,顺理成章的,应该建立两块Survivor区,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续的内存空间,避免了碎片化的发生)。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代中。下图中每部分的意义和上一张图一样,就不加注释了。

16.jpg

上述机制最大的好处就是,整个过程中,永远有一个survivor space是空的,另一个非空的survivor space无碎片。

那么,Survivor为什么不分更多块呢?比方说分成三个、四个、五个?显然,如果Survivor区再细分下去,每一块的空间就会比较小,很容易导致Survivor区满,因此,我认为两块Survivor区是经过权衡之后的最佳方案。

7. 堆内存分配方法:

分配方法说明收集器
指针碰撞内存地址是连续的Serial和ParNew收集器
空闲列表内存地址是不连续的CMS收集器和Mark-Sweep收集器

8. 内存分配安全问题

在分配内存的同时, 存在线性安全问题, 即虚拟机给A线程分配内存过程中, 指针未修改, B线程可能同时使用了同样一块内存。

JVM中有两种解决办法:

  1. CAS, 比较和交换, CAS是乐观锁的一种实现, 所谓乐观锁就是, 每次不加锁而是假设没有冲突而去完成某项操作, 如果因为冲突失败就重试, 知道成功为止, 虚拟机采用cas配上失败重试的方式保证更新操作的原子性。

  2. TLAB, 本地线程分配缓冲:为每一个线程预分配一块内存, JVM在给线程中的对象分配内存时, 首先在TLAB分配, 当对象大于TLAB中的剩余内存或者TLAB内存耗尽时, 在采用上述的CAS进行内存分配

方案1在每次分配时都需要进行同步控制,这种是比较低效的。

方案2是HotSpot虚拟机中采用的,这种方案被称之为TLAB分配,即Thread Local Allocation Buffer。这部分Buffer是从堆中划分出来的,但是是本地线程独享的。

9. 对象的内存布局

对象在内存中存储的布局可以分为三个区域, 对象头(Header), 实例数据(Instance data)和对齐填充(padding).

1. 对象头

对象头包括两部分信息: 一部分是用于存储对象自身的运行数据,如haashCode, GC分代年龄, 锁状态标志, 线性持有的锁, 偏向线程ID, 偏向时间戳等。

另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪一个类的实例。当对象是一个java数组的时候,那么对象头还必须有一块用于记录数组长度的数据,因此虚 拟机可以通过普通java对象的元数据信息确定java对象的大小,但是从数组的元数据中无法确定数组的 大小。

2. 实例数据

存储对象真正有效的信息

3. 对齐填充

这部分并不是必须要存在的, 没有特别的含义, 在jvm中对象的大小必须是8字节的整数倍, 而对象头也是8字节的倍数, 当对象实例数据部分没有对齐时, 就需要通过对齐填充来补全

10. 对象访问的方式

方式优点
句柄稳定, 对象被移动只要修改句柄中的地址
直接指针访问速度快, 节省了一次指针定位的开销
17.png
18.png
分享技术成长
创作不易.gif

这篇关于JVM堆详解, 了解不迷路的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/333068

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof