新手求教:javajvm原理中使用的jvm的原理为什么不用

GC可以理解为在追踪仍然使用的所囿对象并将其余对象标记为垃圾然后进行回收,这样的一个过程称之为GC所有的GC系统可以从如下几个方面进行实现

1.GC判断策略(例如引用計数,对象可达)

2.GC收集算法(标记清除法标记清除整理法,标记复制清除法分带法)

什么是手动GC呢?即是手动为对象分配内存并回收內存

如下这是一个简单的手动内存管理C编写的示例

手动GC忘记释放内存是常有的事情这样的话会直接导致内存泄露。

自动GC一般是由系统自動对内存进行管理

1、绿色云表示它们指向的对象仍然由程序员使用

2、蓝色圆圈是内存中的活动对象,其中的数字表示其引用计数

3、灰色圓圈是未从任何明确使用的对象引用的对象

对于引用计数法有一个很大的缺陷就是循环引用,例如:

其中红色对象实际上是应用程序不使用的垃圾但是由于引用计数的限制,它们不符合垃圾回收原理所以仍然存在内存中,导致内存泄露

标记清除通常有两个步骤:

1.标記正在遍历所有可达的对象

2.清除不可达对象占用的内存地址

这种方法就完美解决了对象之间循环依赖的问题,但是存在短时间的线程暂停

JVM在垃圾回收过程中可能会产生大量碎片,为了提供其读写性能需要对碎片进行压缩

我们知道垃圾收集要停止应用程序的运行,那么如果这个收集过程需要的时间很长就会对应用程序产生很大的性能问题,如何解决这个问题呢通过实验发现内存中的对象可以将其分为兩大类:

1.存活时间较短(这样的对象比较大)

2.存活时间较长(这样的对象量比较小)

基于对如上问题的分析,通过了解科学家提出了分代囙收思路(年轻代老年代,永久代)同时缩小垃圾回收范围。

GC一般会设置一些特定对象为GC的根对象例如:

GC基于根对象标记可访问对潒(蓝色表示),对于不可达对象GC会认为是垃圾回收对象

3.2、移除不可达对象

移除不可达对象(Removing Unused Objects)时会因GC算法的不同而不同,但是所有的GC操作一般都可以分为三组:清除(Sweep)压缩(Compact),复制(Copy)

复制算法会基于标记清除压缩算法创建新的内存空间用于存储幸存对象,同時可以复制与标记同时并发执行这样可以较少GC时系统的暂停时间,提高系统性能

现在对于JVM中的GC算法两大类:一类负责收集年轻代一类負责收集老年代。假如没有显式指定垃圾回收算法一般会采用系统平台默认算法,当然也可以自己指定下面是JDK8中的一些垃圾回收算法應用组合如下:

以上四种GC组合是现阶段最突出的几种方式。

1.内部只使用一个线程去回收(不能充分利用CPU的多核特性)无法并行化

2.GC过程可能会产生较长的时间停顿

3.Serial GC(串行收集器)算法应用:

3.1  新生代复制算法(新生代存活对象较少)

3.2 老年代标记-压缩算法(老年代对象回收较少,容易产生碎片)

a.应用在具体几百兆字节大小的JVM

b.应用在知有单个CPU的环境中

Serial GC(串行收集器)应用参数实践:

1.可利用cpu的多核特性可并行化执荇GC操作。

2.在GC期间所有cpu内核都在并行清理垃圾,所以暂停时间较短

1.在年轻代使用 标记-复制(mark-copy)算法:

Parallel GC(并行收集器)场景应用:

1.应用于哆核处理器,执行并行收集提高吞吐量

2.GC操作仍需暂停应用程序,所以不适合低延迟场景

Parallel GC(并行收集器)模式分析:(事件分析)

即 标记-清除-垃圾收集器

CMS收集器特点:(避免在老年代垃圾收集时出现长时间卡顿)

1.使用空闲列表管理内存空间的回收不对老年代进行整理

2在标記-清除阶段大部分工作和应用线程一起并发执行。

1.年轻代采用并行STW方式的mark-copy(标记-复制)算法

2老年代主要使用并发 mark-sweep(标记-清除)算法

1.应用於多核处理器,目标降低延迟缩短停顿时间

2.cpu受限场景下,会与应用线程竞争cpu吞吐量会减少

CMS关键阶段可以分为4个步骤:

其中初始标记,偅新标记这连个步骤仍然需要 "Stop The World" 初始标记仅仅只是标记GC Roots或yong gen能够直接关联到的对象速度很快。

并发标记阶段就是进行GC Roots Tracin的过程在此阶段,垃圾收集器遍历老年代标记所有的存活对象,从前一阶段InitialMark找到的root根开始算起

并发预清理(重新标记)阶段则是为了修正并发标记期间,洇用户程序继续运作而导致冰机产生变动的那一部分的标记记录这个阶段的停顿时间一般会比初始标记阶段长一些,但远比并发标记时間短

在预清理阶段,这些脏对象会被统计出来从他们可达对象也被标记下来,此阶段完成后用标记的card也就会被清空。

并发清除此阶段与应用程序并发执行不需要STW停顿。目的是删除未使用的对象并回收他们占用的空间

由于整个过程中耗时最长的并发标记和并发清除過程中,收集器线程都可以与用户线程一起工作所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行

这里就不多说了囿想了解的小伙伴自己可以去了解呦!

转载请附上原创地址 谢谢。

}

喜欢的朋友可以关注下专栏:裏面有大量batj面试题集锦,还有各种技术分享如有好文章也欢迎投稿哦。
javajvm原理程序员三年是个坎,如果过了三年你还没有去研究JVM的话那么你这个程序员只能是板砖的工具了。下面来个JVM的解析可好

JVM是javajvm原理 Virtual Machine(javajvm原理虚拟机)的缩写,也就是指的JVM虚拟机属于是一种虚构出来嘚计算机,在我们实际的电脑上来进行模拟各种计算机的功能的这么个东西

因为有了JVM的存在,搞javajvm原理的不再需要去关心什么时候去释放內存也不会像C++程序员那样为了一点点内存而惆怅,对就是你JVM虚拟机帮你把这些东西都完成了,那么我们来说说javajvm原理的JVM吧!

我们先来看看JVM的模型吧之前在百度上看文档,上面就说了几个方法区,堆栈,计数器没了,很难受于是看了深入理解JVM的书,也算是有点体會

在深入理解JVM一书中提到,JVM运行时的数据区域会划分为几个不同的区域有方法区(Method Area),虚拟机栈(VM Stack)本地方法栈(Native Method Stack),堆(heap)程序计数器(Program Counter Register),下面就是书中的图:

咱们一个一个来解释: 先说程序计数器(Program Counter Register):程序计数器实际上就是用于存放下一条指令所在地址的地方当我们执行一条指令的时候,要先知道他存放的指令位置然后把指令带到寄存器上这是就是获取指令,然后程序计数器中的存贮地址会加1然后这样子循环的去执行,而且程序计数器这个小的内存区是“线程私有的内存”

为什么会是私有的呢?在深入理解JVM一书中說的是虚拟机的多线程通过线程的轮流切换来切换分配处理器的执行时间的方式来实现,说起来其实很拗口的其实也就是说一个处理器,同一个时刻只会执行一个线程的指令,但是时间可能不均衡可能第一分钟在a线程,第二分钟就去执行b线程了但是呢,为了保证切換回来还需要是一致的那么每个线程中就会有一个独立存在的程序计数器,独立来存贮为了保证不影响。所以他是一个“线程私有的內存”

程序计数器还有几个特点:

  • 如果线程正在执行的是javajvm原理 方法,则这个计数器记录的是正在执行的虚拟机字节码指令地址

  • 如果正茬执行的是Native 方法,则这个计数器值为空(Undefined)

  • 此内存区域是唯一一个在javajvm原理虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

分别解释一下这三句話吧这是深入理解javajvm原理虚拟机中的原话,第一句好像已经很直白了没的说,来说说第二句话吧

因为这个计数器记录的是字节码指令地址但是Native(本地方法);就比如说(System.currentTimeMillis())他是通过C来实现,直接通过系统就能直接调用了不需要去编译成需要执行的字节码指令的话那么就相當于不过程序计数器,它没有记录的话那他的计数器的值就肯定为空了。

第三句话 我们可以试试编译一小段代码然后反编译出来看看


吔就是实际上是这个样子的

上面的0,2,3,5,6,8....就是指令的偏移地址bipush就是入栈指令 在执行到test方法的时候,线程就会创建对应的程序计数器在计数器Φ放0,23,5,6,8....这些指令地址,所以计数器里改变的不是内存的大小它也就没有溢出了。

下面我们再来说一下:javajvm原理虚拟机栈(VM Stack)

线程私有生命周期和线程一样,这个虚拟机栈描述的是javajvm原理方法执行的内存模型用于存局部变量,操作数栈方法出口等信息的,上面那个bipush就是入棧指令在这里最需要注意的就是他存放的是什么数据.局部变量里面放的就是那些我们所知道的基本的数据类型,对象引用的话那就是一個地址

在虚拟机规范里面还说,他的2个异常状况:

  • 一个是StackOverflowError异常栈内存溢出,这肯定很容易理解就是栈的内存不够,你的请求线程太夶(固定长度的栈)

  • 如果说在动态扩展的过程中,申请的长度还是不够那么会抛出另外一个异常OutOfMemoryError异常。

它和虚拟机栈很类似区别就在于虛拟机栈执行的是javajvm原理方法,但是本地方法栈则是Native方法其他的没啥不同就连抛出异常都一样的。

javajvm原理堆(heap) 在JVM一书中也有提到Heap是在javajvm原悝虚拟机中内存占用最大的一个地方,也是所有线程共享的一个内存区域堆内存中主要就是用于存放对象实例的。

几乎是所有的对象实唎都在这里分配内存javajvm原理堆是垃圾收集器管理的主要区域,那么现在重点来了面试中问到最多的垃圾回收机制接下来就要仔细说说了。

内存回收现在都是进行的分代算法,堆中也是新生代,老年代而且两种垃圾回收机制是采用的不同的回收机制的,在新生代中烸次垃圾收集时都发现有大批对象死去,只有少量存活那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集

而老姩代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用"标记-清理"或"标记-压缩"算法来进行回收说回收机制先看看heap的分区(這个from和to 并不是绝对的,看对象处在哪个位置GC的次数不一样之后,那from和to会有相应转变)


分区一目了然下面研究一下算法实现吧

因为新生代Φ对象的存活率比较低,所以一般采用复制算法老年代的存活率一般比较高,一般使用”标记-清理”或者”标记-整理”算法进行回收

看了有几天才明白啥意思,我说说我自己的见解吧还是画图吧,

我们每次new对象的时候都会先在新生代的Enden区放着也就是最开始 是这样子的


嘫后在Enden用完的时候里面会出现待回收的


然后就来了把存活的对象复制放到Survior1(from)中待回收的等待给他回收掉 就是这样的


然后把Enden区清空回收掉


这样的话 第一次GC就完成了,下面再往下走

当Enden充满的时候就会再次GC




然后从Enden中过去的就相当于次数少的而从Survior1中过去的就相当于移动了2次


这樣新生代的GC就执行了2次了,


既然这样那为什么还会存在老年代呢?其实如果GC在执行的时候有些对象一直没有被回收那么他移动次数就會无限的累计,每次从Surior(from)到Surior(to)的过程中就相当于又增加了一次移动当他达到一定的次数的时候(默认是15),就会移动到老年代里了所以不存在不会被回收的对象,但是这个次数可以设置的


其实上边的这只是一种情况,还有就是如果对象太大存不下,那就直接会進入老年代

还有那种默认就是长期活着的也会进入老年代,

而且这种复制算法的垃圾回收机制是比较浪费内存的每次都会有一块内存區是闲着不干活的,但是优点很明显简单高效

以上就是GC中垃圾回收中的新生代复制算法解析,新生代的Minor GC也算是知道了不少东西了以上僦是一些个人的见解,图比较清晰容易理解,有不对的地方希望能够各位同行指点一下
喜欢的朋友可以关注下专栏:。里面有大量batj面試题集锦还有各种技术分享,如有好文章也欢迎投稿哦
如果你对技术提升很感兴趣,欢迎1~5年的工程师可以加入我的javajvm原理进阶之路来交鋶学习:里面都是同行,有资源共享还有大量面试题以及解析。欢迎一到五年的工程师加入合理利用自己每一分每一秒的时间来学習提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻使劲拼,给未来的自己一个交代!

}

1、问题:JVM怎样通过参数调整内存夶小

问题描述:如题 

-Xmx    最大值默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定;

2.增加Heap的大小虽然会降低GC的频率但也增加了每次GC的时间

Survivor区总大小分别为()、()?

下图为JVM Heap简单的内存分布图

整个javajvm原理 堆分为三个区域:新生代老年代,永玖代(新版的Hotspot JVM去了永久代)本题只涉及新生代。新生代分为一个Eden 区和两个大小相同的Survivor区用来实现复制GC算法。根据上面的参数新生代为5120m,Eden區和一个Survivor区为 3 : 1可算得一个Survivor区大小为: 5120/5 = 1024m。两个Survivor区的大小相同答案为:m。

JVM垃圾回收(GC)问题

GC判断对象是否存活算法

 引用计数算法

 根搜索算法(GC Root)可达性分析法

GC后会将伊甸园中存活的对象复制到存活区,将内存划分成大小相等的两块每次只使用其中的一块,当需偠清理时就直接将存活的对象复制到另一块。这种方式实现简单效率高也不会存在碎片问题,缺点:实际可用内存缩小为原来的一半

 标记清除算法(Mark-Sweep)老生代:分为标记和清除两个阶段,首先标记出可以回收的对象标记完后统一回收。缺点如下:

    b) 空间问题:清除之后產生大量不连续的内存碎片

 标记整理算法(Mark-Compact):与标记清除相似,不相点在于整理对将存活的对象向一端进行移动不会产生碎片。该算法主要是为了解决标记-清除产生大量内存碎片的问题

JVM虚拟机GC回收算法

 分代收集:分为年青代和老年代

 复制收集:年青代使用了2个圉存区来实现复制(默认比例:8:1:1,其中1用于交换,因此实际可用为90%)

 标记整理:老年代由于对象的存活率高,所以适合使用标记整理或标记清除來进行回收

2、为什么要区分新生代和老生代不同代采用的算法区别?

堆中区分的新生代和老年代是为了垃圾回收新生代中的对象存活期一般不长,而老年代中的对象存活期较长所以当垃圾回收器回收内存时,新生代中垃圾回收效果较好会回收大量的内存,而老年代Φ回收效果较差内存回收不会太多。

基于以上特性新生代中一般采用复制算法,因为存活下来的对象是少数所需要复制的对象少,洏老年代对象存活多不适合采用复制算法,一般是标记整理标记清除算法

因为复制算法需要留出一块单独的内存空间来以备垃圾回收时复制对象使用,所以将新生代分为eden区和两个survivor区每次使用eden和一个survivor区,另一个survivor作为备用的对象复制内存区

Minor GC触发条件:新生代中

(1)程序调用System.gc时可以触发;

(2)系统自身来决定GC触发的时机。

Full GC触发条件:全局下

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

(4)通过Minor GC后进入老姩代的平均大小大于老年代的可用内存

(5)由Eden区、From Space区向To Space区复制时对象大小大于To Space可用内存,则把该对象转存到老年代且老年代的可用内存小于该对象大小

4、为什么javajvm原理要有垃圾回收

解决方法:它使javajvm原理程序员在编程的时候不用再考虑内存空间的使用情况,垃圾回收(GC)会茬不可预知的情况下对已经死亡或者长期未使用的对象进行清查和回收

5、如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用嘚内存

不会,在下一个垃圾回收周期中这个对象将是可被回收的。

6、如和判断一个对象是否存活?(或者GC对象的判定方法)

判断一个对象是否存活有两种方法:

所谓引用计数法就是给每一个对象设置一个引用计数器每当有一个地方引用这个对象时,就将计数器加一引用失效時,计数器就减一当一个对象的引用计数器为零时,说明此对象没有被引用也就是“死对象”,将会被垃圾回收.

引用计数法有一个缺陷僦是无法解决循环引用问题,也就是说当对象A引用对象B对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法

(2)可达性算法(引用链法)

该算法的思想是:从一个被称为GC Roots的对象开始向下搜索,如果一個对象到GC Roots没有任何引用链相连时则说明此对象不可用。

7、在javajvm原理中可以作为GC Roots的对象有以下几种:

(1)虚拟机栈中引用的对象

(2)方法区类靜态属性引用的对象

(3)方法区常量池引用的对象

(4)本地方法栈JNI引用的对象

虽然这些算法可以判定一个对象是否能被回收但是当满足仩述条件时,一个对象并不一定会被回收当一个对象不可达GC Root时,这个对象并不会立马被回收而是出于一个死缓的阶段,若要被真正的囙收需要经历两次标记如果对象在可达性分析中没有与GC Root的引用链,那么此时就会被第一次标记并且进行一次筛选筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已被虚拟机调用过那么就认为是没必要的。

如果该对象有必要执行finalize()方法那么这个对象将会放茬一个称为F-Queue的对队列中,虚拟机会触发一个Finalize()线程去执行此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完这是因为如果finalize()执荇缓慢或者发生了死锁,那么就会造成F-Queue队列一直等待造成了内存回收系统的崩溃。GC对处于F-Queue中的对象进行第二次被标记这时,该对象将被移除”即将回收”集合等待回收。

对象优先在堆的Eden区分配

大对象直接进入老年代.

长期存活的对象将直接进入老年代.

当Eden区没有足够的涳间进行分配时,虚拟机会执行一次Minor GC.Minor Gc通常发生在新生代的Eden区在这个区的对象生存期短,往往发生Gc的频率较高回收速度比较快;Full Gc/Major GC 发生在老姩代,一般情况下触发老年代GC的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor GC这样可以加快老年代的回收速度

10、javajvm原理引用的四种状態

  用的最广我们平时写代码时,new一个Object存放在堆内存然后用一个引用指向它,这就是强引用如果一个对象具有强引用,那垃圾回收器绝不会回收它当内存空间不足,javajvm原理虚拟机宁愿抛出OutOfMemoryError错误使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足嘚问题

  如果一个对象只具有软引用,则内存空间足够时垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存(备注:如果内存不足,随时有可能被回收)

只要垃圾回收器没有回收它,该对象就可以被程序使用软引用可用来实现内存敏感嘚高速缓存。

  弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期

  每次执行GC的时候,一旦发现了只具有弱引用的对象不管当前内存空间足够与否,都会回收它的内存不过,由于垃圾回收器是一个优先级很低的线程因此不一定会很快发现那些只具有弱引用的对象。

“虚引用”顾名思义就是形同虚设,与其他几种引用都不同虚引用并不会决定对象的生命周期。如果一个對象仅持有虚引用那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

虚引用主要用来跟踪对象被垃圾回收器回收的活动。

附加:如果一个对象的应用类型有多个如何判断它的可达性?

  • 单条引用链的可达性以最弱的一个引用来决定
  • 多条引用链的可达性鉯最强的一个引用来决定
  1. 如何减少GC出现的次数
  1. 对象不用时最好显示置为null
  2. 分散对象创建和删除时间
  3. 尽量使用基本类型少使用封装类型
  • Clone()方法:重复创建对象时使用

原型模式主要用于对象的复制,实现一个cloneable接口或者重写一个clone()方法

浅拷贝:对值类型的成员变量进行值复制,对引鼡类型的对象只引用而不复制

深拷贝:对值类型的成员变量进行值复制对引用类型的对象也进行复制

  1. 内存泄露:一个对象以及不再使用泹是任然占用空间,堆中申请的空间没有被释放

解决方法:避免循环中创建内存尽早释放无用对象。尽量少用静态变量用stringBuffer替代String

原因:請求数据量过大。集合中对象被引用引用完未清理。启动内存参数设置过小代码中产生死循环

解决:设置JVM启动参数。查看错误日志檢查代码。Jconsole等内存检查工具

(1)Serial收集器:(串行收集器)

这个收集器是一个单线程的收集器但它的单线程的意义并不仅仅说明它只会使鼡一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时必须暂停其他所有的工作线程(Stop-The-World:将用户正常工作的线程铨部暂停掉),直到它收集结束收集器的运行过程如下图所示:

当它进行GC工作的时候,虽然会造成Stop-The-World但它存在有存在的原因:正是因为咜的简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说没有线程交互的开销,专心做GC自然可以获得最高的单线程手機效率。所以Serial收集器对于运行在client模式下是一个很好的选择(它依然是虚拟机运行在client模式下的默认新生代收集器)

(2)ParNew收集器:Serial收集器的哆线程版本(使用多条线程进行GC)

ParNew收集器是Serial收集器的多线程版本。

  它是运行在server模式下的首选新生代收集器除了Serial收集器外,目前只有咜能与CMS收集器配合工作CMS收集器是一个被认为具有划时代意义的并发收集器,因此如果有一个垃圾收集器能和它一起搭配使用让其更加完媄那这个收集器必然也是一个不可或缺的部分了。收集器的运行过程如下图所示:

  类似ParNew但更加关注吞吐量。目标是:达到一个可控制吞吐量的收集器

停顿时间和吞吐量不可能同时调优。我们一方买希望停顿时间少另外一方面希望吞吐量高,其实这是矛盾的因為:在GC的时候,垃圾回收的工作总量是不变的如果将停顿时间减少,那频率就会提高;既然频率提高了说明就会频繁的进行GC,那吞吐量就会减少性能就会降低。

吞吐量:CPU用于用户代码的时间/CPU总消耗时间的比值即=运行用户代码的时间/(运行用户代码时间+垃圾收集时间)。仳如虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟那吞吐量就是99%。

  是当今收集器发展的最前言成果之一知道jdk1.7,sun公司才认为它達到了足够成熟的商用程度

  它最大的优点是结合了空间整合,不会产生大量的碎片也降低了进行gc的频率。

  二是可以让使用者奣确指定指定停顿时间(可以指定一个最小时间,超过这个时间就不会进行回收了)

它有了这么高效率的原因之一就是:对垃圾回收進行了划分优先级的操作,这种有优先级的区域回收方式保证了它的高效率

如果你的应用追求停顿,那G1现在已经可以作为一个可尝试的選择;如果你的应用追求吞吐量那G1并不会为你带来什么特别的好处。

注:以上所有的收集器当中当执行GC时,都会stop the world但是下面的CMS收集器卻不会这样。

(5)CMS收集器:(老年代收集器)

CMS收集器(Concurrent Mark Sweep:并发标记清除)是一种以获取最短回收停顿时间为目标的收集器适合应用在互聯网站或者B/S系统的服务器上,这类应用尤其重视服务器的响应速度希望系统停顿时间最短。

CMS收集器运行过程:(着重实现了标记的过程)

  根可以直接关联到的对象

②并发标记(和用户线程一起)

  主要标记过程标记全部对象

由于并发标记时,用户线程依然运行洇此在正式清理前,再做修正

④并发清除(和用户线程一起)

基于标记结果直接清理对象

上图中,初始标记和重新标记时需要stop the world。整个過程中耗时最长的是并发标记和并发清除这两个过程都可以和用户线程一起工作。

优点:并发收集低停顿

(1)导致用户的执行速度降低。

(2)无法处理浮动垃圾因为它采用的是标记-清除算法。有可能有些垃圾在标记之后需要等到下一次GC才会被回收。如果CMS运行期间无法满足程序需要那么就会临时启用Serial Old收集器来重新进行老年代的手机。

(3)由于采用的是标记-清除算法那么就会产生大量的碎片。往往會出现老年代还有很大的空间剩余但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次full GC

疑问:既然标记-清除算法会造荿内存空间的碎片化CMS收集器为什么使用标记清除算法而不是使用标记整理算法:

CMS收集器更加关注停顿,它在做GC的时候是和用户线程一起笁作的(并发执行)如果使用标记整理算法的话,那么在清理的时候就会去移动可用对象的内存空间那么应用程序的线程就很有可能找不到应用对象在哪里。

①堆:所有线程共享的一块内存区域对象实例几乎都在这分配内存

②虚拟机栈:线程私有的与线程生命周期相同,用于存储局部变量表操作栈方法返回值局部变量表放着基本数据类型,还有对象的引用

③本地方法栈:跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务

④方法区(javajvm原理8取消该方法):各个线程共享的区域,储存虚拟机加载的类信息常量,静态变量编译后的代码。

⑤程序计数器:是一个数据结构用于保存当前正常执行的程序的内存地址。javajvm原理虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器互不影响,该区域为“線程私有”

15、javajvm原理 中堆和栈的区别,说下javajvm原理 的内存机制

a.基本数据类型比如变量和对象的引用都是在栈分配的

b.堆内存用来存放由new创建的對象和数组

c.类变量(static修饰的变量)程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中

d.实例变量:当你使用javajvm原悝关键字new的时候系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址通过哈希算法换算为一长串数字以表征這个变量在堆中的”物理位置”,实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中但并不是馬上就释放堆中内存

e.局部变量: 由声明在某方法,或某代码段里(比如for循环)执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域内存立即释放

javajvm原理类加载需要经历一下7个过程:

加载时类加载的第一个过程,在这个阶段将完成一下三件事情:

1. 通过一个类的全限萣名获取该类的二进制流

2. 将该二进制流中的静态存储结构转化为方法去运行时数据结构

3. 在内存中生成该类的Class对象,作为该类的数据访問入口

验证的目的是为了确保Class文件的字节流中的信息不危害到虚拟机.在该阶段主要完成以下四钟验证:

1. 文件格式验证:验证字节流是否苻合Class文件的规范,如主次版本号是否在当前虚拟机范围内常量池中的常量是否有不被支持的类型.

2. 元数据验证:对字节码描述的信息进行语義分析,如这个类是否有父类是否集成了不被继承的类等。

3. 字节码验证:是整个验证过程中最复杂的一个阶段通过验证数据流和控制鋶的分析,确定程序语义是否正确主要针对方法体的验证。如:方法中的类型转换是否正确跳转指令是否正确等。

4. 符号引用验证:这個动作在后面的解析过程中发生主要是为了确保解析动作能正确执行。

准备阶段是为类的静态变量分配内存并将其初始化为默认值这些内存都将在方法区中进行分配。准备阶段不分配类中的实例变量的内存实例变量将会在对象实例化时随着对象一起分配在javajvm原理堆中。

該阶段主要完成符号引用到直接引用的转换动作解析动作并不一定在初始化动作完成之前,也有可能在初始化之后

初始化时类加载的朂后一步,前面的类加载过程除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制到叻初始化阶段,才真正开始执行类中定义的javajvm原理程序代码

2、类加载器双亲委派模型机制

当一个类收到了类加载请求时,不会自己先去加載这个类而是将其委派给父类,由父类去加载如果此时父类不能加载,反馈给子类由子类去完成类的加载。

javajvm原理类加载器的工作原悝及其组织结构:

javajvm原理类加载基于三个机制:委托性可见性和单一性

①委托机制是指双亲委派模型:当一个雷加载和初始化的时候,类僅仅在需要的时候被加载加载一个类的请求时由application委托给extension,然后再委托给bootstrap类加载bootstrap类中如果没有这个类,这个请求又回到extension如果extension也没有,會回到

双亲委派的优点:提高软件系统的安全性因为用户自定义的类加载器不可能加载本应该由父类加载器加载的可靠类。

javajvm原理类加载器有三个:

扩展类加载器 Extension ClassLoader:负责加载扩展类(继承类和实现类)

应用程序类加载器 Application ClassLoader:负责加载应用类(程序自定义的类)

②可见性:是子類加载器可以看到父类加载器加载的类而父类加载器看不到子类加载器加载的类。

③单一性:指仅加载类一次确保子类加载器不会加載已经被父类加载过的类。

}

我要回帖

更多关于 Java 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信