Java虚拟机
2021-03-29 16:23:06 51 举报
AI智能生成
Java虚拟机(JVM)是Java技术的核心组成部分,它是一个虚拟的计算机,负责执行Java字节码指令。JVM将Java源代码编译成平台无关的字节码,然后在各种操作系统和硬件平台上运行这些字节码。这使得Java程序具有跨平台性,即一次编写,到处运行。JVM还提供了一个运行时环境,用于管理内存、垃圾回收和多线程等资源。此外,JVM还包含了一系列工具和库,如Java标准类库(JCL)和Java安全机制,以支持开发人员构建高质量的Java应用程序。总之,Java虚拟机是实现Java“一次编写,到处运行”的关键组件,它为Java程序提供了稳定、高效的运行环境。
作者其他创作
大纲/内容
内存分配策略<br>
1.对象优先在 Eden 区分配
当Eden区分配没有足够的空间进行分配时,虚拟机将会发起一次Minor GC。<br>如果本次 GC 后还是没有足够的空间,则将启用<font color="#f68b1f"><u><b>分配担保机制</b></u></font>在老年代中分配内存。<br>
2.大对象直接进入老年代
3.长期存活对象将进入老年代
虚拟机加载机制<br>
<u>虚拟机把描述类的数据从Class文件<b><font color="#f15a23">加载</font></b>到内存,并对数据进行<b><font color="#f15a23">校验</font></b>,<b><font color="#f15a23">准备</font></b>、<b><font color="#f15a23">解析</font></b>和<font color="#f15a23"><b>初始化</b></font>,最终形成可以被虚拟机直接使用的java类型。</u>
类装载方式
隐式装载<br>
new一个对象时,隐式调用类装在器,加载类到JVM中<br>
显示装载
通过Class.forname显示装在类到JVM中<br>
<b>为了节省内存开销,Java类加载是动态的,<font color="#f15a23">不会一次性把所有类加载之后再运行,而是保证程序运行的基础类完全加载到JVM中,其他类需要的时候再加载</font></b><br>
类加载器
启动类加载器
加载java核心类库,无法被java程序直接引用;<br>用来加载<b>Java_HOME/lib/</b>目录中的,或者被<b> -Xbootclasspath</b> 参数所指定的路径中并且被虚拟机识别的类库;<br>
扩展类加载器
加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录;<br>java扩展库,加载<b>\lib\ext</b>目录或<b>Java. ext. dirs系统变量指定的路径</b>中的所有类库;<br>
应用类加载器
根据java应用classpath来加载,java的应用类都是由它加载完成<br>
其他加载器<br>
通过<b>继承 java.lang.ClassLoader类</b>的方式实现。
类加载原则与机制
全盘负责<br>
当一个类加载器加载一个类时,该类所依赖的类也都全部由他加载
父类委托<br>
先让父类加载,只有当父类无法加载时才从自己的类路径中加载该类<br>
缓存机制
保证所有加载过的类都被缓存;<br>当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,<br>只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。<br>这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效<br>
执行过程<br>
1.装载
根据查找路径找到相应的 class 文件然后导入;
1.通过一个类的全限定名来获取其定义的二进制字节流;<br>2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;<br>3.在java堆中生成代表这个类的java.lang.Class对象,作为方法区中这些数据的访问入口<br>
2.检验<br>
检查加载的 class 文件的正确性;
文件格式验证<br>
验证字节流是否符合Class文件格式的规范
元数据验证
对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;<br>例如:这个类是否有父类,除了Java.lang.Object之外。<br>
字节码验证
通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的
符号引用验证
确保解析动作能正确执行
3.准备
给类中的静态变量分配内存空间,并将其初始化为默认值<br>(初始化为具体的值在初始化阶段)<br>
这个时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中
该初始化通常情况下是数据类型默认的零值(0,0L,null,false等),而不是被Java代码中显示地赋予值。<br>
如果<u>字段同时被final和static修饰,那么在准备阶段变量value就会被初始化为<b>其制定的值</b></u>
4.解析
虚拟机将常量池中的符号引用替换成直接引用的过程。<br>符号引用就理解为一个标识,而在直接引用直接指向内存中的地址;<br>
5.初始化
对静态变量和静态代码块执行初始化工作
类加载
对于任<font color="#f57c00">意一个类</font>,都需要由<b><font color="#f57c00">加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性</font></b>,<br><u><b>每一个类加载器,都有一个独立的类名称空间。</b></u><br>类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。<br>
双亲委派模型<br>
当一个类收到了类加载请求时,<b>不会自己先去加载这个类,而是将其委派给父类</b>,由父类去加载,<br>如果此时<b>父类不能加载,反馈给子类,由子类去完成类的加载。</b><br>
为了<u><b>防止内存中出现多个相同的字节码</b></u>;<br>因为如果没有双亲委派的话,用户就可以自己定义一个java.lang.String类,那么就无法保证类的唯一性;
<u>打破双亲委派模型</u><br>
自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法;
意义
防止内存中出现多份一样的代码
保证java程序安全运行<br>
JVM调优<br>
调优工具<br>
<font color="#f15a23">jconsole</font><br>
用于对 JVM 中的内存、线程和类等进行监控;
<font color="#f15a23">jvisualvm</font><br>
JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
子主题
常用的 JVM 调优的参数
-Xms2g:初始化堆大小为 2g;<br>
-Xmx2g:堆最大内存为 2g;<br>
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;<br>
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;<br>
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;<br>
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;<br>
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;<br>
-XX:+PrintGC:开启打印 gc 信息;<br>
-XX:+PrintGCDetails:打印 gc 详细信息。
性能问题分析<br>
常见性能问题分析<br>
CPU load过高,导致系统不可用或tps急剧降低<br>
jvm问题导致tps降低<br>
Young GC 次数过多
Full GC次数过多<br>
Oracle preparestatment内存预分配占用过大<br>
Local Cache 内容过大<br>
Full GC 时间长<br>
Perm space回收频繁或者OOM<br>
重复查询泛滥<br>
锁竞争或死锁<br>
连接池连接占用<br>
不合理的使用集合
找到性能瓶颈(工具)<br>
内部实现层面上的优化<br>
让可以并行的方法并行<br>
避免重复查询<br>
减少远程交互次数<br>
批量操作<br>
local cache
设置合理的Young Gen大小<br>
减少Full GC 时间<br>
设置合理线程数
减少模板的大小<br>
架构层面优化<br>
集中式数据Cache<br>
Page Cache
片段 Cache
finalize()/finalization()<br>
finalize()
垃圾回收时调用
finalization()<br>
析构方法,只有在某些很特殊的情况下,<br>比如你调用了一些native的方法(一般是C写的),可以要在finaliztion里去调用C的释放函数。<br>
内存区域<br>
元空间
类加载子系统
运行时数据区(JVM内存)<br>
方法区<br>
存储被虚拟机加载的<b><font color="#f68b1f">类信息</font></b>,<b><font color="#f68b1f">常量</font></b>,<b><font color="#f68b1f">静态变量</font></b>及<b><font color="#f68b1f">及时编译的代码</font></b>等数据<br>
虚拟机栈
存储<b><font color="#f68b1f">局部变量表</font></b>,<b><font color="#f68b1f">操作数栈</font></b>,<b><font color="#f68b1f">动态链接</font></b>,<b><font color="#f68b1f">方法出口</font></b>等<br>
本地方法栈
<u>与虚拟机栈类似,不过虚拟机栈服务java方法,本地方法栈服务native方法服务</u>
堆<br>
被<u><b>所有线程共享</b></u>,<b><u>几乎所有对象实例都在这里分配内存</u></b><br>
程序计数器<br>
<u>当前线程执行字节码的行号指示器</u><br>字节码解析器的工作就是通过改变程序计数器的值,来获取下一条要执行的命令<br>
执行引擎
本地库接口<br>
hotsop虚拟机
对象的创建方式
new<br>(调用构造方法)
Class类的newInstance<br>(调用构造方法)<br>
反射:Constructor的newInstance<br>(调用构造方法)
使用clone
反序列化<br>
对象创建流程
1.类加载:检查常量池是否已加载类,如果没有则先加载类<br>
2.分配内存<br>
两种分配方式
<b>指针碰撞:</b>内存绝对规整使用“指针碰撞”分配内存<br>
<u>堆的内存是规整,即所有<b>用过的内存放在一边,而空闲的的放在另一边</b>。</u><br>分配内存时将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离<br>
<b>空闲列表:</b>内存不规整,从空闲列表分配<br>
堆的内存不是规整的,则需要由<u><b>虚拟机维护一个列表来记录哪些内存是可用的</b></u>,<br>分配的时候可以从列表中查询到足够大的内存分配给对象,并在分配后更新列表记录。<br>
3.划分内存时的并发问题
CAS失败重试
本地线程分配缓存<br>
每个线程在java堆中预先分配一小块内存<br>
4.内存空间初始化操作,初始化内存空间为零,做一些必要的初始化操作(元信息,哈希码)<br>
5.执行初始化方法
对象的访问定位<br>
<u>Java 程序需要通过 JVM 栈上的引用访问堆中的具体对象。对象的访问方式取决于 JVM 虚拟机的实现</u>
两种方式
句柄
句柄指向对象的指针,对象的指针指向对象的地址<br>
<u>堆中划分一块作为<b>句柄池</b></u><br>
句柄中存放对象类型和对象地址<br>
相对慢
<b>优势</b>:引用中存储的是<u><b>稳定</b></u>的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改
直接指针<br>
指向对象在内存中的直接地址<br>
java堆中对象内部需维护对象数据类型<br>
<b>优势:<u>速度更快</u></b>,节省了一次指针定位的时间开销。由于对象的访问在Java 中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。HotSpot 中采用的就是这种方式。<br>
内存溢出
长生命周期对象持短生命周期对象
垃圾回收
<u><b>虚拟机空间或内存不足时触发</b></u>
要点
1.System.gc()是建议垃圾回收,并不一定马上就回收。且触发的是major垃圾回收<br>
2.当永久代满了或到达临界值会触发完全垃圾回收,永久代也会被回收<br>
3.java8移除了永久代,增加了一个<b>元数据区</b>的navtive内存区<br>
元数据区
内存区域
4.程序员不能实时的对某个对象或所有对象调用垃圾回收器进行回收<br>
垃圾回收有<b>分代复制垃圾回收</b>、<b>标记垃圾回收</b>、<b>增量垃圾回收</b>。
5.垃圾回收不会发生在永生代(<u><font color="#b71c1c">Java虚拟机规范中不要求对方法区进行垃圾收集</font></u>)<br>
6.如果永生代满了或者超过临界值,则会触发<b><font color="#f68b1f">完全垃圾回收</font></b>,此时永生代也会被回收(<u><font color="#b71c1c">仅限HotPot虚拟机等实现了永生带垃圾回收的虚拟机</font></u>)<br>
<font color="#f68b1f">正确的永生代大小,对避免完全垃圾回收很重要</font><br>
原理<br>
<b><u>采用有向图的方式记录和管理堆中的对象,通过这种方式分析对象的可达性</u></b>,当确定一些对象不可达时,GC就有职责回收这些垃圾<br>
对象引用类型
强引用
不会被回收<br>
软引用SoftReference
在发生内存泄漏前回收
弱引用
在下一个垃圾回收时回收<br>
虚引用<br>
无法通过虚引用获取对象,虚引用的用途在回收时返回一个通知<br>
判断回收方法
引用计数器
循环引用无法解决
可达性分析<br>(主流)<br>
<font color="#f15a23"><b>GC Roots </b></font>开始向下搜索,搜索所走过的路径称为引用链。<br>当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。<br>
可以作为GC Roots的对象<br>
虚拟机栈中引用的对象
方法区<b>静态属性引用的变量</b><br>
本地方法栈JNI引用的对象<br>
JNI 句柄,包括 JNI Local Handles 和 JNI Global Handles。
在类中定义常量与静态变量,<br>在<b>方法中定义局部变量</b>,这些都是堆中对象的起点<br>
当前活跃线程的栈桢里指向堆中对象的引用,即当前所有<b>正在被调用方法的引用类型参数、局部变量等</b>
<b>类的引用类型静态变量</b>,这里指的是引用类型,像 int 等基本数据类型的静态变量肯定不能作为 GC Roots
当前所有已被加载的Java类和类加载器
<span data-slate-object="text" data-key="54"><span data-slate-leaf="true" data-offset-key="54:0" data-first-offset="true"><span data-slate-string="true">在<b>方法区中常量引用的对象</b>,譬如字符串常量池 ( String Table ) 里的引用</span></span></span>
所有<b>被同步锁 ( synchronized关键字 ) 持有的对象引用。</b>
满足上述条件的时候,不会马上被回收,它还可以自救,<br><b>一个对象真正的死亡至少要进行两次标记</b><br>
1.标记所有不可达对象,并进行筛选,筛选的标准是该对象覆盖了finalize()方法且finalize()方法没有被虚拟机调用过,选出的对象将被放置在一个“即将被回收”的队列中。稍后虚拟机会创建一个低优先级的Finalizer线程去遍历队列中的所有对象并执行finalize()方法
2.对队列中的对象进行第二次标记,如果对象<b>在finalize()方法中重新与引用链上的任何一个对象建立关联</b>,那么这个对象将被移除队列,而还留在队列中的对象,就会被回收了
垃圾回收算法
标记回收法
<u>标记无用对象,然后进行清除回收</u>
两个阶段
1.标记可回收
2.清除可回收<br>
优点
实现简单,不需要对象移动
缺点
标记清楚效率低,产生大量碎片内存,增加垃圾回收频率<br>
收集器
CMS(Concurrent Mark Sweep)收集器<br>
是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收<br>器非常适合。在启动 JVM 的参数加上“<font color="#f15a23"><b>-XX:+UseConcMarkSweepGC</b></font>”来指定使用 CMS 垃圾回收器。
复制清除法<br>
<u>按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉</u>
优点
按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点
可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
收集器<br>
Serial收集器<br>
ParNew收集器<br>
Parallel Scavenge收集器
G1(Garbage First)收集器(<b>整堆回收器</b>)
标记整理法
<u>标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存</u>
优点
解决了标记-清理算法存在的内存碎片问题<br>
缺点
仍需要进行局部对象移动,一定程度上降低了效率<br>
收集器
Serial Old收集器<br>
Parallel Old收集器<br>
分代算法<br>
根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,<font color="#f15a23"><b>新生代基本采用复制算法</b></font>,<font color="#f15a23"><b>老年代采用标记整理算法</b></font>
分代
新生代(1/3)
Eden(8/10)<br>
From Survivor(1/10)<br>
To Survivor(1/10)
<u>基本采用复制算法</u>
老年代(2/3)
<u>采用标记整理算法,标记清除法</u>
永生代(方法区)
Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,<br>而且在方法区进行垃圾收集的“性价比”一般比较低<br>
回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,<br>假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“abc”常量就会被系统“请”出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。<br>
永久代的垃圾收集主要回收两部分内容:<b><font color="#e57373">废弃常量</font></b>和<font color="#e57373"><b>无用的类</b></font><br>
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。<br>类需要同时满足下面3个条件才能算是“无用的类”:<br><b>该类所有的实例都已经被回收</b>,也就是Java堆中不存在该类的任何实例。<br><b>加载该类的ClassLoader已经被回收</b>。<br><b>该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法</b><br>虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而不是和对象一样,不使用了就必然会回收。是否对类进行回收,HotSpot虚拟机提供了<b>-Xnoclassgc</b>参数进行控制,还可以使用<b>-verbose:class</b>及<b>-XX:+TraceClassLoading</b>、 <b>-XX:+TraceClassUnLoading</b>查看类的加载和卸载信息。<br>在<b style=""><font color="#e57373">大量使用反射、动态代理、CGLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出。</font></b><br>
具体描述<br>
分代回收器有两个分区:老生代和新生代,<font color="#f15a23"><b>新生代默认的空间占比总空间的 1/3</b>,<b>老生代的默认占比是 2/3</b></font>。<br>
新生代使用的是复制算法,<b><font color="#f15a23">新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1</font></b>,
执行流程
1.新生代复制算法<br>
<ol><li>把Eden、From Survivor存活的对象发去To Survivor分区</li><li>清空Eden、From Survivor分区</li><li>From Survivor分区与To Survivor分区交换</li></ol>
2.每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当<b>年龄到达 15(默认配置是 15)时,升级为老生代</b>。<br><b>大对象也会直接进入老生代</b>。<br>
在1.5.0_05之前最大值可以设置为31 ,<br>1.5.0_06以后最大值可以设置为15,<font color="#c41230"><b>超过15会被认为无限大。</b></font><br>
3.老年代空间到达某个值的时候就会触发全局垃圾回收<br>
对象年龄计数规则
如果对象在 Eden 区出生,并且能够被 Survivor 容纳,将被移动到 Survivor 空间中,这时设置对象年龄为 1。<br>对象在 Survivor 区中每「熬过」一次Minor GC 年龄就加 1,当年龄达到一定程度(默认 15) 就会被晋升到老年代。<br>
垃圾收集器
新生代收集器<br>
Serial(复制算法) <br>
新生代单线程收集器,标记和清理都是单线程,优点是简单高效
-XX:+UseSerialGC
<b>新生代、老年代</b>使用<b>串行</b>回收;新生代复制算法、老年代标记-压缩
PraNew(复制算法)
Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现
-XX:+UseParNewGC ParNew收集器<br>-XX:ParallelGCThreads 限制线程数量
<b>新生代并行</b>,<b>老年代串行</b>;新生代复制算法、老年代标记-压缩
Parallel Scavenge(复制算法)
追求高吞吐量,高效利用 CPU
吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),高吞吐量可以高效率的利用CPU时间,<b>尽快完成程序的运算任务</b>
适合后台应用等<u><b>对交互响应要求不高</b></u>的场景
和ParNew的最大区别是<font color="#f15a23"><u>GC自动调节策略</u></font>;<font color="#f15a23">虚拟机会根据系统的运行状态收集性能监控信息,动态设置这些参数</font>,以提供最优停顿时间和最高的吞吐量
-XX:+UseParallelGC
使用Parallel收集器+ 老年代串行;新生代复制算法、老年代标记-压缩
老年代收集器<br>
Serial Old(标记-整理算法)
单线程收集器,Serial收集器的老年代版本
Parallel Old(标记-整理算法)
并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本
-XX:+UseParallelOldGC
使用Parallel收集器+ 老年代并行
CMS(Concurrent Mark-Sweep<b><font color="#f15a23">标记-清除算法</font></b>)
并行收集器,以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,<font color="#c41230"><b>追求最短GC回收停顿时间</b>。</font>
对于要求服务器响应速度的应用上,这种垃圾回收器非常适合
<font color="#c41230"><b>收集结束会产生大量空间碎片</b></font>,<br>当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,<br><u><b>临时 CMS 会采用 Serial Old 回收器进行垃圾清除</b>,此时的性能将会被降低</u><br>
-XX:+UseConcMarkSweepGC 使用CMS收集器<br>-XX:+ UseCMSCompactAtFullCollection Full GC后,进行一次碎片整理;整理过程是独占的,会引起停顿时间变长<br>-XX:+CMSFullGCsBeforeCompaction 设置进行几次Full GC后,进行一次碎片整理<br>-XX:ParallelCMSThreads 设定CMS的线程数量(一般情况约等于可用CPU数量)
新生代使用ParNew,老年代CMS
步骤
初始标记(STW)
标记GC roots能直接关联到的对象
并发标记<br>
重新标记(STW)
并发清除
G1收集器
回收整个java堆(标记-整理算法)
Java堆并行收集器,G1收集器是<b>JDK1.7提供的一个新收集器</b>,G1收集器基于“标记-整理”算法实现,<u><b>不会产生内存碎片</b></u>。<br>
不会产生空间碎片,可以精确地控制停顿;
<font color="#c41230">回收的范围是<b>整个Java堆(</b>包括新生代,老年代)</font>,而<b>前六种收集器回收的范围仅限于新生代或老年代。</b>
G1将整个堆分为大小相等的多个Region(区域),G1跟踪每个区域的垃圾大小,<u><b>在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的区域,已达到在有限时间内获取尽可能高的回收效率;</b></u>
特点<br>
G1收集器基于“<font color="#c41230"><b>标记-整理</b></font>”算法实现,<b>不会产生内存碎片</b>。<br>分配大对象时不会因为无法找到连续空间而提前触发下一次GC。
<b>可预测停顿</b>,这是G1的另一大优势,降低停顿时间是G1和CMS的共同关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
分布式垃圾回收(DGC)
RMI 使用 DGC 来做自动垃圾回收。<br>因为 RMI 包含了跨虚拟机的远程对象的引用,垃圾回收是很困难<br>的。DGC 使用<b><font color="#c41230"><u>引用计数算法</u></font></b>来给远程对象提供自动内存管理。
垃圾收集器组合策略
可以搭配使用的垃圾回收器
Minor GC<br>Major GC(Full GC)<br>
Minor Gc
发生在新生代的 GC,<br>因为 Java 对象大多都是朝生夕死,所以 Minor GC <b>非常频繁</b>,一般回收<b>速度也非常快</b><br>
Major GC<br>(Full GC)<br>
是指发生在老年代的 GC,<br>出现了 Major GC 通常会<b>伴随至少一次 Minor GC</b>。Major GC 的速度通常会<b>比 <font color="#f68b1f">Minor GC 慢 10 倍以上</font></b><br>
堆栈区别
堆
物理地址分配对象不连续,性能较慢<br>
主要存放<b>对象实例</b>和<b>数组</b>,<u>关注数据结构</u><br>
整个应用程序共享可见<br>
运行期确认大小
栈
使用数据结构中的栈,先近后出,物理地址分配连续,性能较快<br>
主要存放<b>局部变量</b>,<b>操作数栈</b>,<b>返回结果</b>,<u>关注程序执行</u><br>
线程私有,当前线程可见,生命周期同线程<br>
编译期确认大小
java程序运行机制
1.IDEA开发java源文件
2.编译器将Java源文件编译为字节码文件<br>
3.java虚拟机类加载器,加载字节码文件到运行时数据区的方法区类,<br>并在堆上创建一个java.lang.Class对象用来封装类在方法区的数据结构<br>
4.执行引擎把字节码文件翻译成系统命令,再交由cpu执行<br>这个过程会调用其他语言的本地接口
参考资料资料
软引用和弱引用
软引用和弱引用
0 条评论
下一页