JVM中的双亲委派,新生代老年代。

本文仅用于个人观看,文章内容来源于其他博客的整理以及自己的理解有传送门。

如果内容有侵犯个人权益,麻烦联系我删除此文章。

图片来源于B站老杜的JVM讲解。

一、一个类被初始化加载的过程

二、继承中的类初始化过程

(静态成员变量初始化时基于在代码中的排序。)

父类-- 静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块、


父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器

        在类加载阶段通过一个类的全限定明来获取描述该类的二进制字节流这个动画,代码被称为类加载器,这个动作是可以自定义来实现的

2.1JVM中的类加载器

 类在加载的过程中是通过从下往上查找,加载的过程是从上往下加载。

向上委派



如何理解向上委派?

一个类在收到类加载请求后,不会自己加载这个类,而是把这个类加载请求向上委派给它的父类去完成,父类收到这个请求后又继续向上委派给自己的父类,以此类推,直到所有的请求委派到启动类加载器中。

tips: 这让双亲委派机制保证类加载的顺序性。

第一步: 将自定义加载器挂载到应用程序类加载器
第二步: 应用程序类加载器将请求委托给扩展类加载器
第三步: 扩展类加载器将请求委托给启动类加载器

双亲委派的核心:保证类的安全性和唯一性

 向下委派
如何理解向下委派
当父类加载器在接收到类加载请求后,发现自己也无法加载这个类(这个情况通常是因为这个类的Class文件在父类的加载路径中不存在)这时父类会把这个信息反馈给子类,并向下委派子类加载器来加载这个类,直到这个请求被成功加载,但是一直到自定义加载器都没有找到,JVM就会抛出ClassNotFund异常。

在这里插入图片描述

向上委派机制保证先加载JDK的核心类,再加载应用程序的类,有效防止了因为应用程序中因为某个类的存在一些不安全问题,导致JVM变得不安全。

向下委派机制保证需要加载的类,都得到了加载。

在这里插入图片描述

————————————————
版权声明:本文为CSDN博主「究极酸菜鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41318400/article/details/109279871

沙箱机制


我们创建一个自定义string类,但是在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class),报错信息说没有main方法,就是因为加载的是rt.jar包中的string类。这样可以保证对java核心源代码的保护,这就是沙箱安全机制。

Tomcat为什么要打破双亲委派机制
首先tomcat是一个web容器,主要是需要解决以下问题

一个web容器可能要部署两个或多个应用程序,不同的应用程序之间可能会依赖同一个第三方类库的不同版本,因此要保证每个应用程序的类库都是独立的、相互隔离的
部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类库被加载进JVM中
web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
web容器支持jssp文件修改后不用重启,jsp文件也要编译成.class文件的,支持HotSwap功能
Tomcat使用Java默认加载器的问题
默认的类加载器无法加载两个相同类库的不同版本,它只在乎类的全限定类名,并且只有一份,所以无法解决上面的问题1和问题3,也就是相关隔离的问题。

同时在修改jsp文件后,因为类名一样,默认的类加载器不会重新加载,而是使用方法区中已经存在的类,所以需要每个jsp对应一个唯一的类加载器,当修改jsp的时候,直接卸载唯一的类加载器,然后重新创建类加载器,并加载jsp文件。

在这里插入图片描述

————————————————
版权声明:本文为CSDN博主「zzy9607」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43884884/article/details/107719529

通过自定义类加载器继承ClassLoader类重写loadClass方法

二、新生代老年代。

JVM的分布

- 方法区和堆属于线程共享区。

方法区(元空间):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

堆:

 (Java Heap)是虚拟机所管理的内存中最大的一块,在虚拟机启动时创建。 此内存区域的唯一目的就是存放对象实例 ,几乎所有的对象实例都在这里分配内存,但是随着JIT编译器的发展和逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上就变得不是那么绝对了。

原文链接:https://blog.csdn.net/sun337939896/article/details/79092356

- 虚拟机栈、本地方法栈、程序计数器属于线程私有区

JVM通过类加载器将class文件放在元空间(永久代)中,

局部变量放在虚拟机栈中,main线程也放在虚拟机栈中,

对象放在堆里面,

程序计数器,记录我们代码记录到第几行,

本地方法栈,调用C++语言写的代码,执行用native修饰的类和方法;

类如果不用了可以从元空间中卸载掉。

JVM中程序计数器的特点和作用

1.程序计数器是一块较小的空间,可以忽略不计。

2.是当前线程所执行的字节码行号指示器

3.Java多线程运行时,每条线程都有一个独立的程序计数器,各条线程之间程序计数器互不影响。

4.该区域属于线程私有,每个线程独立储存。

5.该区域不存在OutOfMemory

6.无GC回收。

一个线程的生命周期就是一个栈的生命周期。

JVM中堆的作用和特点。

1.线程共享的一段区域

2.虚拟机启动时创建。

3.虚拟机中最大的一块内存区域。

4.存放对象或者实例数组。

5.GC垃圾回收器主要管理区域。

6.可以分为老年代和新生代

7.新生代课细分为Eden(伊田园)区、Survivor01(幸存区)区、Survivor02区

Java通过可达性分析算法来判断对象是否存活

        该算法的基本思路是通过一系列成为GC Roots 的跟对象作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程所经过的路径称为应用链,如果某个对象到GC Roots间没有任何引用链项链,称为不可达。证明此对象是不可能再被使用的对象,就可以被垃圾回收器回收。

那些对象可以作为GC Roots呢

1.虚拟机栈(栈帧的本地变量表)中的引用对象,各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等所引用的对象

2.元空间中的类静态属性引用的对象。

3.元空间中的常量引用的对象。

4.本地方法栈中的Native方法引用的对象。

5.Java虚拟机内部的引用,入基本数据类型对应的Class对象,一些常驻的异常对象NullPointException、OutOfMeroryError等。还有系统类加载器。

6.所有被同步锁synchronize持有的对象。

Java中有不同的引用类型,分为强引用、软引用、弱引用、和虚引用。

强引用:Object o = new Object();

软引用:SoftReference内存重组时不回收,内存不足时回收。

弱引用:WeakReference不管内存是否充足只要GC一运行就会回收。

虚引用:PhantomReference很少使用,就像没有引用一样,其作用就是该引用对象被GC回收时候出发一个系统通知,或者出发进一步的处理。

JVM堆内存模型:年轻代、老年代。

大部分对象朝生夕死,少数对象长期存活。

JVM里垃圾回收针对的是新生代、老年代、还有元空间(永久代)

不会针对方法的栈帧进行回收,方法一旦执行完毕、栈帧出栈、里面的局部变量就直接从内存中清理掉。也就是虚拟机栈不存在垃圾回收。

代码中创建出来的对象分为两种。

短期存活的,分配在堆中,使用完之后就会被垃圾回收。

一种是长期存活的。需要一直生存在Java堆内存中,让程序不停的去使用。

短期存活的对象在Java的堆内存中的新生代中,

长期存活的对象通过再S0区和S1区来回被垃圾回收15次(一次成长一岁)后,进入Java堆内存的老年代中。

Java中中的动态年龄问题

当从低岁数 中加到高岁数如果幸存区的内存大于50%,则从最后一个年龄后算起全放入老年代中。(例如1+2+3>50%则从4岁之后的幸存区中的对象全部放入老年代中。)

Java的老年代担保机制 

        如果老年带可用空间大于新生代的对象总大小,那么就可以放心的去调用Minor GC去进行一个垃圾回收即使所有对象存活S区放不下也能直接放入老年代中,如果小于新生代对象的大小,那么会再做一次判断判断老年代所剩余区域的大小是否大于之前每一次Minor GC后进入老年代的对象的平均大小,如果大于,那么JVM会冒险的调用一次MinorGC,Minor GC后S去放不下,老年代也放不下那么会触发FullGC去回收老年代中的对象,如果小茹每次MInor GC的平均大小,那么会直接触发FullGC。

        所以老年代担保机制是为了避免频繁的进行Full GC4.

        如果Full GC之后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么此时就会导致"OOM”(OutOfMerory)内存溢出;

 

 

 

 

因为年轻代和老年代不同的特点,需要采用不同的垃圾回收算法;

        年轻代的对象,它的特点是创建之后很快就会被回收,所以需要用一-种垃圾回收算法;
        老年代的对象,它的特点是需要长期存活,所以需要另外一种垃圾回收算法;
所以需要分成两个区域来放不同的对象;

新生代:

        绝大多数对象都是朝生夕灭的; .
如果一个区域中大多数对象都是朝生夕灭,那么把它们集中放在一起, 每次回收时只关注如何保留少量存活对象,而不是去标记那些大量将要被回收的对象,就能以较低的代价回收到大量的空间;

老年代

如果是需要长期存活的对象,那把它们集中放在一块, 虚拟机便可以使用较低的频率来回收这
个区域,这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用;
 

为什么要有两个幸存区:因为每次触发Minor GC时,会判断Eden区和Survivor的对象是否需要回收,如果Eden区的对象需要存活则放入幸存区,幸存区的对象也不需要被回收,则会出现内存碎片,导致内存不连续。所以会有两个幸存区,将需要存活的对象做一个复制放入另一个幸存区中。

        JVM划分出新生代。老年代之后,垃圾收集器可以每次只回收其中某一个或者某些部分的
区域,同时也有了"Minor GC”"Major GC" "Full GC"这样的回收类型的划分;
Minor GC/Young GC :新生代收集
Major GC/Old GC:老年代收集
Full GC:整堆收集,收集整个Java堆和元空间/方法区的垃圾收集;
Mixed GC:混合收集,收集整个新生代以及部分老年代的垃圾收集,目前只有G1收集器会有这种行为。

-清除算法

标记清除算法时JVM中最基础的收集算法。

        标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,
标记出所有存活的对象,在标记完成后,统一回收所有未被标记的对象,标记过程就是对象是
否属于垃圾的判定过程,基于可达性分析算法判断对象是否可以回收;

        

标记清除算法的优点:基于可达性算法,实现简单,后续算法都是基于标记清除算法实现的。

缺点: 执行效率不稳定,如果Java堆中包含大量的对象,而且大部分对象是需要被回收的,这时必须进行大量的标记和清楚的动作,导致标记和清楚阶段会随着对象数量的增加效率会逐步降低。

内存碎片太多的问题:内存碎片越多,可能会导致以后程序再运行过程中,需要分配较大的对象时候找不到连续的内存不得不触发另一次垃圾回收。

标记-复制算法

将可用的内存分为大小相等的两部分,每次只使用其中的一部分,当这一块的内存用完了,就将还存活的对象复制到另外一块内存上,再把已经使用过的内存空间一次性全部清除掉。

标记复制算法的优点:

实现简单,效率高。解决了标记-清除算法所导致的内存碎片过多的问题。

缺点:

1.将内存分为1:1的两部分,内存利用率太低。空间浪费过大。

 2.一般情况下,虚拟机采用的是标记-复制算法来回收新生代,但是JVM对复制算法进行了改进,JVM并没有按照1:1的比例来划分新生代的内存空间,因为通过大量的统计和研究,90%以上的对象都是朝生夕死的。所以JVM将新生代分为了一块较大的Eden区和两块小的Survivor,每次分配内存只使用Eden区和其中的一块Survivor区,当发生垃圾回收是,将Eden区和Survivor中任然存活的对象一次性复制到另一块Survivor空间上,然后直接清理掉Eden去和已使用的Survivor空间上。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1。即每次新生代的可用空间为新生代总空间的90%。我们每次不能保证存活的对象数量只有新生代的10,所以JVM有一个空间担保机制。当幸存区空间不足容纳一次MinorGC之后存活的对象时,就将存活的对象按照空间担保机制放入老年区中。

标记整理算法

 

        标记-清除算法会导致产生大量的内存碎片,而老年代这种每次都有大量存活对象的区域,移动这些存活对象,并更新对象的引用,无疑是一种比较耗时的工作。而这种对象移动操作会暂停用户的应用程序才能进行,像这样的停顿被称为是Stop the world STW

        即使移动对象是比较耗时的工作,但是为了内存中有连续的空间我们不得不这么做,如果不这么做如果为了存一个比较大的对象的时候,又会影响到对象的分配和访问的效率,所以JVM在权衡利弊之后采用了标记-整理算法。对内存进行一个整理。

        CMS垃圾收集器针对于老年代,采用的是标记-清除算法。暂时容忍内存碎片的存在。直到内存空间碎片化达到一定程度时候才会采用标记-整理算法。

优点:

1.不会将内存分为两个区域,造成内存浪费。

2.解决了内存碎片化的问题。

缺点:

效率问题,除了想标记-清除算法的过程外,还多了一步整理的过程,效率降低。

分代收集算法

 分代手机算法是一种思想,现在一般的虚拟机都采用分代收集算法,

例如java的JVM根据内存存活的不同周期,将堆内存分为新生代和老年代,根据不同年代的特点采用不同的算法。

新生代:每次垃圾回收都会有大量的对象死亡,有少量的对象存活,因此采用复制算法,只需要复制少量的存活对象,就可以完成手机。

老年代:因为对象存活率较高,采用标记-清除和标记整理算法来进行回收。

 

 

 

 

 

 



 

 

 

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值