16-02、JVM系列之:内存与垃圾回收篇(二)

JVM系列之:内存与垃圾回收篇(二)

##本篇内容概述:
1、堆Heap Area
2、方法区Method Area
3、运行时数据区总结
4、对象的实例化内存布局和访问定位

一、堆 Heap Area

1、堆的核心概念

·一个JVM实例只存在一个堆内存,堆也是Java内存管理的核心区域。·Java堆区在JVM启动的时候即被创建,其空间大小也就被确定下来了。是JVM管理的最大一块内存空间。·堆可以处于物理上不连续的内存空间中,但在逻辑上他应该被视为连续的。·所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区TLAB(Thread Local Allocation Buffer)
【所以说堆空间不是完全共享的,因为还有TLAB】·堆内存的大小是可以调节的:初始堆空间-Xms10m 最大堆空间-Xmx10m##解释:
一个进程对应一个JVM实例,一个JVM实例对应一个Runtime Data Area,
一个进程对应一个堆和方法区,一个进程有多个线程,每个线程对应一个PC寄存器、虚拟机栈和本地方法栈。
因此,堆和方法区在进程中会被多个线程共享。·几乎所有的对象实例 以及 数组 都应该在运行时分配在堆上。·数组和对象可能永远不会存储在栈上,因为栈帧中保存应用,这个引用指向对象或者数组在堆中的位置。·在方法结束后,堆中的对象不会马上被移除,仅仅在GC垃圾收集的时候才会被移除。·堆,是GC垃圾回收期执行垃圾回收的重点区域##拓展
堆中创建对象的命令 : 创建对象new  创建数组newarray

在这里插入图片描述

2、堆的内存细分

##堆空间细分为:JDK7及之前:新生区 + 养老区  +  永久区JDK8及之后:新生区 + 养老区  +  元空间【新生区 = Eden区 + Survivor区】(现代垃圾收集器大部分都是基于分代收集理论设计)名称: 新生区 = 新生代 = 年轻代养老区 = 老年区 = 老年代永久区 = 永久代

在这里插入图片描述

3、设置堆内存大小与OOM

·Java堆区用于存储Java对象实例,那么堆的大小在JVM启动时就已经设定好了。可以通过-Xmx -Xms来进行设置:
> -Xms表示堆区的起始内存,等价于-XX:InitialHeapSize
> -Xmx表示堆区的最大内存,等价于-XX:MaxHeapSize·一旦堆区中的内存大小超过-Xmx所指定的最大内存时,就会抛出OutOfMemoryError异常·通常会将-Xms 和 -Xmx 两个参数配置相同的值:
其目的是为了能够在java垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小,从而提升性能。
(堆的起始内存 如果 不等于 堆的最大内存,在使用的过程中会不断的 扩容 和 释放,造成了不必要的系统压力,也避免了GC后的重新分隔堆区大小)·默认情况下
初始内存大小 = 物理电脑内存大小 / 64
最大内存大小 = 物理电脑内存大小 / 4##拓展:
-X 是JVM的运行参数  ms是memory start##查看设置的参数: 
方式一: 查看进程jps  /   查看内存使用 jstat -gc 进程ID
方式二:-XX:+PrintGCDetails    打印GC详情
[堆区对象存储的时候 Eden + s0或s1  幸存者区s0和s1只能使用其中的一个]##下图S0 S1表示幸存者区0和1,S0U S1U表示幸存者区是use使用大小
##EC和EU表示伊甸园Eden区的大小

在这里插入图片描述

4、新生代与老年代

·存储在JVM中的Java对象可以被划分为两类:
>一类是生命周期较短的瞬时对象,这类对象的创建消亡都非常迅速
>另一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的声明周期保持一致。·Java堆区进一步细分的话,可以划分为 "年轻代"(YoungGen) 和 "老年代"(OldGen)·其中 "年轻代" 又可以划分为: Eden空间、Survivor0空间、Surivivor1空间(有时也可叫做from区、to区)

在这里插入图片描述

·下面这些参数开发中一般不会调----配置新生代与老年代在堆结构的占比:>默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的1/3
>可以修改-XX:NewRatio=4,表示新生代占1,老年代占4,新生代占整个堆的1/5·在HotSpot中,Eden空间 和 另外两个Survivor空间的所占比例是8:1:1·当然开发人员可以通过选项 -XX:SurvivorRatio 调整空间比例。
如:-XX:SurivivorRatio=8·几乎所有的Java对象都是在Eden区被new出来的。·绝大部分的Java对象的 销毁 都是在 新生代 进行了(80%)·可以使用选项 "-Xmn" 设置新生代最大内存大小
>这个参数一般使用默认值就可以了(如果同时设置了-XX:NewRatio,以-Xmn为准)·JVM会默认开启 自适应 内存分配策略
>我们可以通过 -XX:-UseAdaptiveSizePolicy 来关闭自适应
[-XX:+UseAdaptiveSizePolicy表示使用  -XX:-UseAdaptiveSizePolicy表示关闭]

5、对象的分配过程

1> new的对象先放伊甸园区。此区有大小限制2> 当伊甸园区的空间填满时,程序此时需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC / YGC)。
将伊甸园区中不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区。
(垃圾回收期GC时,STW[Stop the World],Java应用程序的其他所有线程都会被挂起,以方便垃圾回收器判断谁该被回收)
【伊甸园区满了会触发YGC,但是S0S1幸存者区满了不会触发YGC,而是会直接晋升到老年代】3> 然后将伊甸园区中的剩余对象移动到幸存者0区4> 如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有被回收,就会放到幸存者1区。同时伊甸园区幸存下来的也放到幸存者1区。5> 如果再次经历垃圾回收,此时会重新放回幸存者0区(伊甸园幸存者也是),接着再去幸存者1区(伊甸园幸存者也是)6> 啥时候能去养老区呢?可以设置次数:默认是15次参数-XX:MaxTenuringThreshold=<N>进行设置7> 在老年代,相对悠闲。当老年代内存不足时,再次出发GC(Major GC),进行老年区的内存清理。8> 若老年区执行了Major GC之后发现依然无法进行对象的保存,就会产生OOM异常(java.lang.OutOfMemoryError : Java heap space)

在这里插入图片描述

##总结:·幸存者S0S1区,复制之后有交换,谁空谁是to·关于垃圾回收:频繁在新生区收集,很少在养老区收集,几回不在永久区/元空间收集

在这里插入图片描述

##拓展:常用的调优工具·JDK命令:jinfo  jstat  jmap  javap等
·Eclipse:Memory Analyzer Tool
·Jconsole
·JVisualVM
·Jprofiler
·Java Flight Recorder
·GCViewer
·GC Easy

6、Minor GC、Major GC与Full GC

·JVM在进行GC时,并非每次都对上面三个内存区域一起回收(新生代、老年代、方法区),
大部分时候回收的都是 新生代·针对HotSpot VM的实现,GC按照回收区域又分为两种大类型:部分收集(Partial GC)整堆收集(Full GC)>部分收集:不是完整收集整个JAVA堆的垃圾收集。其有细分为:
新生代收集(Minor GC/Young GC):只是新生代的垃圾收集
老年代收集(Major GC/Old Gc):只是老年代的垃圾收集
混合收集(Mixed GC):收集整个新生代以及部分老年代的垃圾收集
[注意:只有CMS GC会有单独收集老年代的行为]
[注意:很多时候Major GC会和Full GC混淆起来一块使用,需要具体分辨是老年代回收还是整堆回收]
[注意:只有G1 GC会有Mixed GC]>整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集·Minor GC(年轻代GC)触发机制:
>当年轻代空间不足时,会出发Minor GC,这里的年轻代满了是指Eden代满,Survivor满不会出发GC(每次Minor GC会清理年轻代的内存)
>因为Java对象大多都是朝生夕灭的特性,所以MinorGC非常频繁,一般回收速度也比较快。
>Minor GC会引发STW,暂停其他用户的线程,等垃圾回收技术,用户线程才恢复。·Major GC(老年代GC)触发机制:
>Major GC指发生在老年代的GC,对象从老年代被清理。
>出现Major GC,会经常伴随至少一次的Minor GC(但非绝对)
[也就是老年代空间不足时,会出尝试出发Minor GC。如果之后空间还不足则出发Major GC]
>Major GC的速度一般会比Minor GC慢10倍以上,STW的时间更长。
>如果Major GC后,内存还不足,就报OOM。·Full GC触发机制:
>触发Full GC的情况有如下五中:
①、调用System.gc()时,系统执行Full GC,但是不必然执行
②、老年代空间不足
③、方法区空间不足
④、有Eden区、S0区向S1区复制时,对象大小大于S1可用内存,则把该对象转存到老年代,且老年代的可用内存小于对象大小。
[注意:Full GC是开发或者调优中尽量要避免的,这样暂时时间会短一些]

7、堆空间分代思想

##为什么需要把JAVA堆分代?不分代就不能正常工作了吗?·经研究,不同对象的声明周期不同。70%~90%的对象是临时对象。
>新生代:有Eden、两块大小相同的Survivor构成
>老年代:存放新生代中经历多次GC仍然存活的对象·其实部分带完全可以,分代的唯一理由就是优化GC性能。
如果没有分代,那所有的对象都在一块,就如同吧一个学校的人都关在一个教室。GC的时候要找
到哪些对象没用,这样就会对堆的所有区域进行扫描。而很多对象都是朝生夕死的,如果分代的
话,吧新创建的对象放到某一地方,当GC的时候先把这块存储 朝生夕死 对象的区域进行回
收,这样就会腾出很大的空间

8、内存分配策略

·如果对象在Eden出生并经过一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被
移动到Survivor空间中,并将对象年龄设为1.对象在Survivor区中每熬过一次MinorGC,
年龄就增加1岁,当它的年龄增加到一定程度(默认15)时,就会被晋升到老年代。
[对象晋升老年代的年龄阈值,可以通过-XX:MaxTenuringThreshold来设置。]·针对不同年龄段的对象分配原则如下:
>优先分配到Eden
>大对象直接分配到老年代[尽量避免程序中出现过多的大对象][大到Eden区的剩余空间放不下了,只能直接放到 老年区]
>长期存活的对象分配到老年代
>动态对象年龄判断如果s区中相同年龄的所有对象大小的综合大于s空间的一般,年龄大于或等于该年龄的对
象可以直接进入到老年代,无需等到MaxTenuringThreshold中要求的年龄
>空间分配担保-XX:HandlePromotionFailure###空间分配担保:在发生Minor GC 之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。
>如果大于,则此次Monor GC是安全的
>如果小于,则虚拟机会查看-XX:HandlePromotionFailure设置值是否允许担保失败
如果HandlePromotionFailure=true,name会继续检查老年代最大可能连续空间是否大于历次晋升到老年代的对象的平均大小。
√如果大于,则尝试进行Minor GC,但这次Minor GC依然是有风险的
√如果小于,则改为进行一次Full GC
如果HandlePromotionFailure=false,则改为进行一次Full GC在JDK6 Update24之后,HandlePromotionFailure参数不会再影响到虚拟机的空间分配担保策略。规则变为了 只要老年代的连续空间大于新生代对象总大小 或者 历次晋升的品骏大小就会进行MinorGC,否则将进行FullGC

9、对象分配过程:TLAB

##什么是TLAB?
·从内存模型而不是垃圾回收的角度,堆Eden区域继续进行划分,JVM为每个线程分配了一个私有缓冲区域,它包含在Eden空间内。
·多线程同时分配内存是,使用TLAB可以避免一系列的非线程安全问题,同时还能够提升内存分
配的吞吐量,因此我们可以将这种内存分配方式称之为 快速分配策略
·OpenJDK衍生出来的JVM都提供TLAB设计。##为什么有TLAB(Thread Local Allocation Buffer)?
·堆区是线程共享区域,任何线程都可以访问到堆区中的共享数据
·由于对象实例的创建在JVM中非常频繁,因此在并发环境下从堆区中划分内存空间是线程不安全的
·为避免多个线程操作统一地址,需要使用加锁等机制,进而影响分配速度,所以要有TLAB##TLAB说明
·不是所有的对象实例都够在TLAB中成功分配内存,但JVM确实是将TLAB作为内存分配的首选
·在程序中,可以通过-XX:UseTLAB设置是否开启TLAB(默认开启)
·默认情况下,TLAB空间的内存非常小,进栈整个Eden空间的1%,当然我们可以通过
-XX:TLABWasteTargetPercent设置TLAB空间所占Eden空间百分比大小
·一旦对象在TLAB空间分配内存失败时,JVM就会尝试着通过使用 加锁机制 确保数据操作的原子性,从而直接在Eden空间中分配内存。

在这里插入图片描述

10、堆空间的参数设置

-XX:+PrintFlagsInitial	查看所有的参数的默认初始值
-XX:+PrintFlagsFinal	查看所有的参数的最终值具体查看某个参数的指令:jps 查看当前运行中的进程jinfo -flag SurvivorRatio 进程ID
-Xms	初始堆空间内存(默认屋里内存1/64)
-Xmx	最大堆空间内存(默认屋里内存1/4)
-Xmn	设置新生代大小(初始值及最大值)
-XX:NewRatio	配置新生代与老年代在堆结构的占比
-XX:SurvivorRatio	配置新生代中Eden和S0/S1空间的比例
-XX:MaxTenuringThreshold	设置新生代垃圾的最大年龄
-XX:+PrintGCDetails	输出GC处理日志打印GC简要信息:-XX:+PrintGC  -verbose:gc
-XX:HandlePromotionFailure	设置空间分配担保

11、堆是分配对象存储的唯一选择吗?

如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。·如何将堆上的对象分配到栈,需要使用逃逸分析手段
·这是一种可以有效减少java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。
·通过逃逸分析,java Hotspot编译器能够分析出一个新的对象的引用的使用范围从而决定是否要将这个对象分配到堆上。·逃逸分析的基本行为就是分析对象动态作用域:
>当一个对象在方法中被定义后,对象只在方法内部使用,责备认为没有发生逃逸。
>当一个对象在方法中被定义后,他被外部方法所引用,则认为发生逃逸。
例如作为调用参数传递到其他地方中

结论:开发中能使用局部变量的,就不要在方法外定义。

12、逃逸分析:代码优化

使用逃逸分析,编译器可以对代码做如下优化:
>栈上分配。
将堆分配转化为栈分配。
>同步省略。
如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步
>分离对象或标量替换
有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分或全部可以不存储在内存,而是存储在CPU寄存器中

二、方法区

1、栈、堆、方法区的交互关系

在这里插入图片描述

2、方法区的概述

·方法区还有一个别名叫做Non-Heap(非堆),尽管所有的方法区在逻辑上是属于堆的一部分。
所以方法区可以看做是一块独立于java堆的内存空间。·方法区(Method Area)与java堆一样,是各个线程共享的内存区域。·方法区在JVM启动的时候被创建,并且他的实际的屋里内存空间中和堆区一样都是不连续的
·方法区的大小和堆空间一样,可以选择固定大小或者可扩展·方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出。
虚拟机同样会抛出内存溢出错误:java.lang.OutOfMemoryError:PermGen space或
者java.lang.OutOfMemoryError:Metaspace·关闭JVM就会释放这个区域的内存
##方法区演进Java7及以前,习惯上把方法区称之为永久代。
Java8开始,使用了元空间取代了永久代
【Java8以后,是用元空间来实现的方法区;在Java8之前,则是用永久代实现的方法区】【元空间使用的是 本地内存  非JVM内存】
【原来永久代使用的是JVM的内存,现在元空间改用 本地内存】

在这里插入图片描述

3、设置方法区大小与OOM

##Java7及之前:
-XX:PermSize 设置永久代初始分配空间。默认20.75m
-XX:MaxPermSize 设置永久代最大可分配空间 32位机默认64M,64位机默认82M
当JVM加载的类信息容量超过了这个值会报OutOfMemoryError:PermGen Space
【-XX:PermSize=100m  -XX:MaxPermSize=500M】##Java8及之后:
-XX:MetaspaceSize 设置元空间初始分配空间 默认21M
-XX:MaxMetaspaceSize 设置元空间最大可分配空间 默认-1,没有限制
当JVM加载的类信息容量超过了这个值会报OutOfMemoryError:Metaspace Space
【-XX:MetaspaceSize=100m  -XX:MaxMetaspaceSize=500M】默认-XX:MetaspaceSize为21MB,这就是初始的高水位线,一旦触及这个水位线,
Full GC将会被触发并卸载没用的类(即这个类对应的类加载器不再存活),然后这个高水
位线将会重置。新的高水位线的值取决于GC后释放了多少元空间。如果释放的空间不足,那
么在不超过MaxMetaspaceSize时,适当提高该值、如果释放空间过多,则适当降低该值如果初始化的高水位线设置过低,上述高水位线调整情况会发生很多次。通过垃圾回收期的日
志可以观察到Full GC多次调用。为了避免频繁地GC,减一将XX:MetaspaceSize设置为
一个相对较高的值

如何解决OOM

1、要解决OOM异常或heap space异常,一般的手段是首先通过内存映射分析工具堆dump
出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底
是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)2、如果内存泄漏(Memory Leak),可以进一步通过工具查看泄漏对象到GC Roots的引用
链。于是就能找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动
回收他们的。掌握了泄漏对象的类型信息,以及GC Roots引用链的信息,就可以比较准确地
定位出泄漏代码的位置。3、如果不存在内存泄漏,换句话说就是内存中的对象却是都还必须存活着,那就应当检查虚
拟机的堆参数(-Xmx与-Xms),与机器屋里内存对比看看是否还可以调大,从代码上检查是
否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

4、方法区的内部结构

·方法区的存储内容:
>类型信息(域信息、方法信息)、常量、静态变量、即使编译器编译后的代码缓存等##一、类型信息
堆每个加载的类型(类class\接口interface\枚举Enum\注解annotation),JVM必须在方法区中存储一下类型信息:
>这个类型的完整有效名称(全名=包名.类名)
>这个类型直接父类的完整有效名
>这个类型的修饰符(public abstract final的某个子集)
>这个类型直接接口的一个有序列表##二、域Field信息(成员变量)
JVM必须在方法区中保存类型的所有域相关信息以及域的声明顺序
>域的相关信息包括:域名城、域类型、域修饰符(public private protected static final volatile transient的某个子集)##三、方法信息
JVM必须保存所有方法的一下信息,同域信息一样包括声明顺序:
>方法名称
>方法的返回类型(或void)
>方法参数的数量和类型(按顺序)
>方法的修饰符(public private protected static final synchronized abstract native的一个子集)
>方法的字节码、操作数栈、局部变量表及大小
>异常表(每个异常处理的开始位置、结束为止、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引)·non-final的类变量
>静态变量和类关联在一起,随着类的加载而加载,他们成为类数据在逻辑上的一部分
>类变量被类的所有实例共享,即使没有类实例时也可以访问
>全局常量:static final,被声明为final的类变量的处理方法则不同,每个全局常量在编译的时候就会被分配了。

在这里插入图片描述

##四、运行时常量池 VS 常量池
·方法区:内部包含了 运行时常量池runtime constant pool
·字节码文件:内部包含了 常量池constant pool
[注:字节码文件内的常量池,被加载到了方法区中就叫做运行时常量池]常量池表Constant Pool Table,包括了各种 字面量 和对类型、域和方法的"符号引用"。·为什么需要常量池?
一个java源文件中的类、接口,编译后产生一个字节码文件。而java中的字节
码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种
方式,可以存到常量池,这个字节码包含了指向常量池的引用。在动态链接的时
候会用到运行时常量池。
如:
public class SimpleClass{public void sayHello(){System.out.println("hello");}
}
以上虽然代码很小,但是里面却使用了String\System\PrintSream及
Object等结构。这里的代码量其实已经很小了,如果代码多,引用到的结构会
更多!这里就需要常量池了!
(我们在class字节码文件里,不会引入string、system、printstream、Object等的源文件,只需引入其引用即可-->符号引用)·常量池中有什么?
常量池内存储的数据类型包括:数量值、字符串值、类引用、字段引用、方法引用常量池可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。·运行时常量池
>运行时常量池是方法区的一部分
>常量池是class文件的一部分,用于存放编译器生成的各种字面量与符号引
用,这部分内容将在类加载后存放到方法区的运行时常量池中
>运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池
>JVM为每个已加载的类型(类或接口)都维护一个常量池。池中的数据就像数
组一样,是通过索引访问的
>运行时常量池中包含多种不同的常量,包括编译器就已经明确的数值字面量,
也包括到运行期解析后才能够获得的方法或字段引用,此时不再是常量池中符号
地址了,这里换位真实地址。
>运行时常量池类似于传统编程语言中的符号表,但是它所包含的数据却比符号
表更加丰富些
>当创建类或接口的运行时常量池时,如果狗仔运行时常量池所需的内存空间超
过了方法区所能提供的最大值,则JVM会抛出OutOfMemoryError异常

实例:

public class MethodArea {public static void main(String[] args) {int x=500;int y=100;int a=x/y;int b=50;System.out.println(a+b);}}

编译后的文件

Classfile /D:/WorkSpace/DailyCodePackage/basic/target/classes/com/lee/jvm/rundataarea/MethodArea.classLast modified 2020-12-7; size 670 bytesMD5 checksum 42390faabe2fc63519914b0ac436af5fCompiled from "MethodArea.java"
public class com.lee.jvm.rundataarea.MethodAreaminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #5.#25         // java/lang/Object."<init>":()V#2 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;#3 = Methodref          #28.#29        // java/io/PrintStream.println:(I)V#4 = Class              #30            // com/lee/jvm/rundataarea/MethodArea#5 = Class              #31            // java/lang/Object#6 = Utf8               <init>#7 = Utf8               ()V#8 = Utf8               Code#9 = Utf8               LineNumberTable#10 = Utf8               LocalVariableTable#11 = Utf8               this#12 = Utf8               Lcom/lee/jvm/rundataarea/MethodArea;#13 = Utf8               main#14 = Utf8               ([Ljava/lang/String;)V#15 = Utf8               args#16 = Utf8               [Ljava/lang/String;#17 = Utf8               x#18 = Utf8               I#19 = Utf8               y#20 = Utf8               a#21 = Utf8               b#22 = Utf8               MethodParameters#23 = Utf8               SourceFile#24 = Utf8               MethodArea.java#25 = NameAndType        #6:#7          // "<init>":()V#26 = Class              #32            // java/lang/System#27 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;#28 = Class              #35            // java/io/PrintStream#29 = NameAndType        #36:#37        // println:(I)V#30 = Utf8               com/lee/jvm/rundataarea/MethodArea#31 = Utf8               java/lang/Object#32 = Utf8               java/lang/System#33 = Utf8               out#34 = Utf8               Ljava/io/PrintStream;#35 = Utf8               java/io/PrintStream#36 = Utf8               println#37 = Utf8               (I)V
{public com.lee.jvm.rundataarea.MethodArea();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 3: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcom/lee/jvm/rundataarea/MethodArea;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=3, locals=5, args_size=10: sipush        5003: istore_14: bipush        1006: istore_27: iload_18: iload_29: idiv10: istore_311: bipush        5013: istore        415: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;18: iload_319: iload         421: iadd22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V25: returnLineNumberTable:line 6: 0line 7: 4line 8: 7line 9: 11line 10: 15line 11: 25LocalVariableTable:Start  Length  Slot  Name   Signature0      26     0  args   [Ljava/lang/String;4      22     1     x   I7      19     2     y   I11      15     3     a   I15      11     4     b   IMethodParameters:Name                           Flagsargs
}
SourceFile: "MethodArea.java"

5、方法区的演进

版本变化
jdk1.6及之前有永久代,静态变量存放在永久代
jdk1.7有永久代,但已将字符串常量池、静态变量移除,保存在堆中
jdk1.8及之后无永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆

在这里插入图片描述

##永久代为什么被元空间所替代
·永久代设置空间大小很难确定
在某些场景下,如果动态加载类过多,容易产生Perm的OOM。元空间和永久代之间的最大区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制·对永久代进行调优很困难
Full GC时间长,造成长时间的STW,影响程序的效率##StringTable为什么要调整
JDK7将StringTable字符串常量池放在了堆中,因为永久代的回收效率很低,在
Full GC的时候才会触发。而Full GC是老年代的空间不足、永久代不足时才会触
发。这就导致了StringTable的回收效率不高。而我们开发中会有大量的字符串被创
建,回收效率低,导致永久代内存不足。放到堆中,能即使回收内存。

6、方法区的垃圾回收

方法区的GC主要包含两部分内容:常量池中废弃的常量 和 不再使用的类型

三、运行时数据区总结

在这里插入图片描述

思考如下:
##一、说一下JVM内存模型,有哪些区,分别干什么?##二、JAVA8的内存分代改进?##三、栈和堆的区别?堆的结构?为什么两个survivor区?##四、Eden和Survivor的分配比例?##五、JVM内存分区,为什么要分新生代、老年代和持久代?##六、什么时候对象会进入老年代?##七、JVM永久代中会发生垃圾回收吗?

四、对象的实例化内存布局与访问定位

对象的实例化:

1、创建对象的方式:
>new
>Class的newInstance
>Constructor的newInstance(xxx)
>使用clone()
>使用反序列化
>第三方库Objenesis2、创建对象的步骤:
>判断对象对应的类是否加加载、链接、初始化
虚拟机遇到一条new指令,首先去检查这个指令的参数能否在metaspace的常量池中定位
到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化。
如果没有,那么在双亲委派模式下,使用当前类加载器以classLoader+包名+类名为
key进行查找对应的.class文件。如果没有找到文件,则抛出
classNotFoundException异常,如果找到,则进行类加载,并生成对应的Class类
对象。>为对象分配内存
首先计算对象占用空间的大小,接着在堆中划分一块内存给新对象。如果实例成员变量是引用变量,仅分配引用变量空间即可(4个字节大小)>处理并发安全问题
采用CAS失败重试、区域加锁保证更新的原子性
每个线程预先分配一块TLAB>初始化分配到的空间
所有属性设置默认值,保证对象实例字段在不赋值时可以直接使用>设置对象的对象头
将对象的所属类、对象的HashCode和对象的GC信息、锁信息等数据存储在对象的对象头中。这个过程的具体设置方式取决于JVM实现>执行init方法进行初始化
初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。

对象的内存布局:

1、对象头Header
>运行时元数据
哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
>指向类元数据InstanceKlass,确定该对象所属的类型
即指向了元空间或者方法区中的对象的所属具体类型
(注:如果是数组,还需记录数组的长度)2、实例数据Instance data
它是对象真正存储的有效信息,包括程序代码中定义的各种类型的字段(包括从父类集成下来的和本身拥有的)3、对齐填充
不是必须的

对象的访问定位:

在这里插入图片描述

对象访问方式主要有两种:
>句柄访问(浪费空间且效率低)
栈帧中的refrence指向对应的堆空间的句柄池,句柄池有两部分组成,一个是到对象实
例数据的指针指向堆中对象的实例数据,另一个是到对象类型数据的指针指向方法区的对
象类型数据>直接指针(HotSpot采用的)(节省空间速度快)
栈帧中的refrence直接指向对象的实例数据,对象的实例数据通过到对象类型数据的指
针指向方法区中的对象类型数据
思考如下:
##一、对象在JVM中是怎么存储的?##二、对象头信息里面有哪些东西?##三、JVM是如果通过栈帧中的对象引用访问到其内部的对象实例的?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/485823.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android studio 读写文件操作(应用场景二)

android studio版本&#xff1a;2023.3.1 patch2 例程&#xff1a;readtextviewIDsaveandread 本例程是个过渡例程&#xff0c;如果单是实现下图的目的有更简单的方法&#xff0c;但这个方法是下一步工作的基础&#xff0c;所以一定要做。 例程功能&#xff1a;将两个textvi…

【算法】——前缀和(矩阵区域和详解,文末附)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;前缀和模版 二&#xff1a;前缀和模版2 三&#xff1a;寻找数组的中心下标 四&#x…

数字图像处理(11):RGB转YUV

&#xff08;1&#xff09;RGB颜色空间 RGB颜色空间&#xff0c;是一种基于红色、绿色、蓝色三种基本颜色进行混合的颜色空间&#xff0c;通过这三种颜色的叠加&#xff0c;可以产生丰富而广泛的颜色。RGB颜色空间在计算机图像处理、显示器显示、摄影和影视制作等领域具有广泛应…

nodejs33: react中的IndexedDB 原有API+操作库idb+数据库事务

在 React 中使用 IndexedDB 作为本地数据库存储可以有效地管理大量的数据&#xff0c;比如缓存、离线功能或状态持久化。可以通过索引进行快速查询&#xff0c;支持事务处理&#xff0c;并且异步操作。 特点&#xff1a; 存储键值对。 支持事务。 数据可以分层组织为数据库、…

创造未来:The Sandbox 创作者训练营如何赋能全球创造者

创作者训练营让创造者有能力打造下一代数字体验。通过促进合作和提供尖端工具&#xff0c;The Sandbox 计划确保今天的元宇宙是由一个个创造者共同打造。 2024 年 5 月&#xff0c;The Sandbox 推出了「创作者训练营」系列&#xff0c;旨在重新定义数字创作。「创作者训练营」系…

Linux---对缓冲区的简单理解--第一个系统程序

前序&#xff1a; 首先先理解一下什么是回车与换行&#xff1b;回车和换行是两个概念&#xff0c;它们不是一个东西&#xff1b; 回车:光标回到开始&#xff1b;换行:换到下一行&#xff1b; 如下图&#xff1a; 行缓冲区 如何理解缓冲区问题&#xff1f; 可以认为&#xff0…

线程和进程(juc)

线程 一&#xff1a;概念辨析 1&#xff1a;线程与进程 进程&#xff1a; 1&#xff1a;程序由指令和数据组成&#xff0c;指令要执行&#xff0c;数据要读写&#xff0c;就需要将指令加载给cpu&#xff0c;把数据加载到内存&#xff0c;同时程序运行时还会使用磁盘&#x…

[计算机网络] HTTP/HTTPS

一. HTTP/HTTPS简介 1.1 HTTP HTTP&#xff08;超文本传输协议&#xff0c;Hypertext Transfer Protocol&#xff09;是一种用于从网络传输超文本到本地浏览器的传输协议。它定义了客户端与服务器之间请求和响应的格式。HTTP 工作在 TCP/IP 模型之上&#xff0c;通常使用端口 …

selenium常见接口函数使用

博客主页&#xff1a;花果山~程序猿-CSDN博客 文章分栏&#xff1a;测试_花果山~程序猿的博客-CSDN博客 关注我一起学习&#xff0c;一起进步&#xff0c;一起探索编程的无限可能吧&#xff01;让我们一起努力&#xff0c;一起成长&#xff01; 目录 1. 查找 查找方式 css_s…

新址启新程 宜宾考拉悠然入驻宜宾市大数据产业园

12月4日&#xff0c;宜宾考拉悠然科技有限公司入驻宜宾市大数据产业园&#xff0c;此次喜迁新址&#xff0c;标志其在宜宾业务步入崭新阶段。 2020年&#xff0c;考拉悠然联合四川省人工智能研究院&#xff0c;结合宜宾人工智能科研、产业发展需要&#xff0c;共同孵化了宜宾考…

使用Redis Stream偶发空指针问题

问题描述&#xff1a;使用redission客户端封装的stream消息队列&#xff0c;在进行消息轮询时&#xff0c;偶发出现空指针问题。 [2024-11-13 09:59:20] [] [] [redis-stream-consumer-thread-1 ] [lambda$streamMessageListenerContainer$1] [ERROR] [c.r.c.r.s.config.Redi…

2024年11月HarmonyOS应用开发者高级认证 最新题库

新增单选 1.下述代码片段中的renderGroup属性&#xff0c;对性能的影响是什么&#xff1a;A A.劣化 B.不一定 C.没有变化 D.优化 2.在刷新Image组件内容时&#xff0c;如果观察到画面会闪一下白块&#xff0c;要怎样优化才能避免白块儿出现&#xff0c;同时又不会卡住画面…

大语言模型应用Text2SQL本地部署实践初探

自从两年前OpenAI公司发布ChatGPT后&#xff0c;大模型(Large Language Model&#xff0c;简称LLM)相关技术在国内外可谓百家争鸣&#xff0c;遍地开花&#xff0c;在传统数据挖掘、机器学习和深度学习的基础上&#xff0c;正式宣告进入快速发展的人工智能(Artificial Intellig…

Leetcode—1539. 第 k 个缺失的正整数【简单】

2024每日刷题&#xff08;206&#xff09; Leetcode—1539. 第 k 个缺失的正整数 C实现代码 class Solution { public:int findKthPositive(vector<int>& arr, int k) {int missing 1;int cur 1;int n arr.size();int missingCnt 0;int ptr 0;for(; missingCn…

PSAI海报设计新选择!StartAI Flux文生图一键生成创意海报!

设计师们&#xff0c;是时候展现你们的创意魔力了&#xff01;StartAI的Flux文生图功能&#xff0c;这款专为设计领域打造的创意工具&#xff0c;正等待着为你的圣诞节海报带来无限灵感与活力&#xff01; 想象一下&#xff0c;你的圣诞节海报不再局限于传统的元素和布局&…

Django模板系统

1.常用语法 Django模板中只需要记两种特殊符号&#xff1a; {{ }}和 {% %} {{ }}表示变量&#xff0c;在模板渲染的时候替换成值&#xff0c;{% %}表示逻辑相关的操作。 2.变量 {{ 变量名 }} 变量名由字母数字和下划线组成。 点&#xff08;.&#xff09;在模板语言中有…

使用Java将PDF文件解析成Excel文件

安装pom依赖 <!-- 解析pdf--><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.27</version> <!-- 请检查并使用最新版本 --></dependency>测试读取pdf文件…

K8S,StatefulSet

有状态应用 Deployment实际上并不足以覆盖所有的应用编排问题&#xff1f; 分布式应用&#xff0c;它的多个实例之间&#xff0c;往往有依赖关系&#xff0c;比如&#xff1a;主从关系、主备关系。 还有就是数据存储类应用&#xff0c;它的多个实例&#xff0c;往往都会在本地…

如何使用Java编写Jmeter函数

Jmeter 自带有各种功能丰富的函数&#xff0c;可以帮助我们进行测试&#xff0c;但有时候提供的这些函数并不能满足我们的要求&#xff0c;这时候就需要我们自己来编写一个自定义的函数了。例如我们在测试时&#xff0c;有时候需要填入当前的时间&#xff0c;虽然我们可以使用p…

单例模式的析构学习

1、例子 如果单例对象是类的static成员&#xff0c;那么在程序结束时不会调用类的析构函数&#xff0c;如下&#xff1a; #include <iostream> using namespace std;class A{ private:static A* m_ins;//声明&#xff0c;静态指针成员A(){} public:static A* getIns(){…