经验整理-1-JVM-内存模型-内存分配-收集器-类加载-100-@

7 篇文章 0 订阅

目录

 

 

-----------------堆外内存与堆内内存区别-----------------

----------逃逸分析与标量替换栈上分配的配合--默认开启------

----------java 中new一个对象占多少内存----------

-------------------new1.8-------------《深入理解Java虚拟机:JVM高级特性与最佳实践》周志明--

?讲一下拉圾回收过程原理?

?新生代与老年代的拉圾回收机制?

GC调优步骤(调优至99%吞吐量)

?你平时是怎么进行tomcat调优的?(新生代使用并行收集器ParNew(无碎片,停顿不明显,吐量高),老年代使用并行标记整理收集器CMS )

?三种JVM内存溢出怎么解决?

 

GC是在什么时候,对什么东西,做了什么事情?

-------------------Old-------------  

巧记:

?说说JVM内存模型及作用?

?说说对垃圾回收机制的了解?

JVM调优主要就是调整下面两个指标

?年轻代和老年代分别选择什么选择器,分别介绍一下优点?

-------------GC算法-------------

-------------------类加载、字节码相关-------------

?类的生命周期?

Java 类加载过程?(JVM加载class文件的原理机制)

启动类加载器有三种?

双亲委派模型是什么?怎么破坏双亲委派模型?

----------类加载器怎么实现热部署----------

Java对象创建过程

-------------类加载机制-------------

双亲委托模式的弊端

这样就出现了一个问题:如何在父加载器加载的类中,去调用子加载器去加载类?

打破双亲委派机制

 

-------------------需要复习的题-------------

 


 

-----------------堆外内存与堆内内存区别-----------------


https://blog.csdn.net/ZYC88888/article/details/80228531

一、堆内内存---分配在堆内,并且会被gc收集掉
引用计数器法(Reference Counting)
标记清除法(Mark-Sweep)
复制算法(Coping)
标记压缩法(Mark-Compact)
分代算法(Generational Collecting)
分区算法(Region)

二、堆外内存---对象分配在堆以外的内存,即本地直接内存,受操作系统管理(而不是虚拟机JVM),不会被频繁YoungGC(所以很少停顿,但fullGC会清理掉它,只不过它不占堆内存,所以fullGc也少)
java.nio.DirectByteBuffer对象进行堆外内存的管理和使用

使用堆外内存的优点
1、减少了垃圾回收--保持一个较小的堆对象数量,以减少垃圾收集
因为垃圾回收会暂停其他的工作。
2、加快了复制的速度
jvm里的堆内存在flush到远程本地内存时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存本身就是本地直接内存,不经过jvm管理。

堆外内存缺点:
而福之祸所依,自然也有不好的一面:
  1 堆外内存难以控制,如果内存泄漏,那么很难排查
  2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

应用场景:
Session会话缓存,保存不激活的用户session,比如用户没有正常退出,我们也无法确定他会不会短时间内再回来,将其会话存到堆外内存。一旦再次登录,无需访问数据库可再次激活。
计算结果的缓存,大量查询的结果等,击中率比较低的都可以迁移到堆外(以免堆外内存泄漏)。

----------逃逸分析与标量替换栈上分配的配合--默认开启------

https://blog.csdn.net/qq_42709262/article/details/84800251

一、逃逸分析

①逃逸分析原理---判断当前方法内的对象会不会再返回给其他线程或别的栈帧用,出去了的话,就是逃逸对象,该对象就不会进行栈上分配

一、栈上分配
①栈上分配原理------将单线程独享的的私有对象(不会再返回给其他线程或别的栈帧用,由于栈上分配拆散后,该对象在本方法结束会自行销毁)的各字段变量拿出来,放入当前线程当前方法对应的栈帧里(标量替换),不再把整个对象放入堆中,用不着,只需要用其字段就行了。
以局部变量的形式分配在栈上也就是打散,这个操作称为:标量替换

栈上分配有什么好处?
不需要GC介入去回收这个对象,出栈即释放资源,可以提高性能原理:栈上分配自已释放了对象,减少垃圾回收的次数
3.怎么开启栈上分配?
在vm中执行以下命令,堆大小取决于项目,这里仅仅是测试
-server -Xms10m -Xmx10m -XX:+PrintGC -XX:+DoEscapeAnalysis -XX:+UseTLAB -XX:+EliminateAllocations
其中,
-XX:+DoEscapeAnalysis :启用逃逸分析(默认打开)
-XX:+EliminateAllocations :标量替换(默认打开)

----------java 中new一个对象占多少内存----------

空对象占八个字节(空对象只有对象头),对象的引用占四个字节,4byte+8byte=12byte,java中的内存是以8的倍bai数来分配的,所以分配的内存是16byte

new Object()和new Integer()都是这样

普通对象头在32位系统上占用8bytes,64位系统上占用16bytes。

 

-------------------new1.8-------------《深入理解Java虚拟机:JVM高级特性与最佳实践》周志明--

?讲一下拉圾回收过程原理?

示例1,拉圾回收过程原理:(执行过程如上图根据执行日志顺序)
第一步,新生代内存分配区域,eden空间100%满后,无法分配内存。
第二步,gc开始回收、保留一部分剩余存活对象存放survivor(假设to区),利用copy复制算法,把from的剩余存活对象转到to区,始终保持一个(from)survivor为empty。并为转移对象age+1.
第三步,之间一直循环上述步骤,当age满足很大的时候触发老年代gc回收保存到old 老年代区。(老年这里还不是full gc,还没满)
第四步,由于不断会有对象进入老年代,老年代内存会一直增大加上新生代达到临界值内存最大值。触发full gc进行整体回收。

疑问点:那你没有说明元空间啊?元空间干啥的呢?
元空间存放:class文件、静态对象、属性等。而且在永久代的时候默认大小256m。但是在元空间jdk8后,它是jvm根据需要动态加载大小(可能是上限是全部剩余内存,可拓展,节省了内存空间)。
 

每次存活对象的判断原理:通过可达性分析去判断对象是被引用,不被引用的被第一次标记(当然CMS和G1可能是2步才标记),然后会判断对象,如果重写了finalize(),会被放入F-Query队列,然后执行finalize(),重新恢复引用链。finalize执行过会有标记,如果下次回收,不会再执行finalize。

?新生代与老年代的拉圾回收机制?

jdk8环境下
底层实现:新生代采用复制算法,老年代采用标记-清除算法(cms是,g1是标记-整理)。
默认使用UseParallelGC= Parallel Scavenge(新生代)+ Serial Old(老年代).
一般我们使用:ParNew(新生代)+ CMS(老年代(用基于"标记-整理"算法的Serial Old作为替补-,解决这个问题:当产生大量的内存碎片,使剩余内存不能满足程序时报错就采用Serial Old))
如果要控制吞吐量体验,只用:G1(新生代+老年代)

注:
parallel scavenge 与parnew 区别-----parallel scavenge 可以设置最大gc停顿时间(-XX:MaxGCPauseMills)以及gc执行时间占比(-XX:GCTimeRatio)
Serial Old与CMS区别-----G1独立管理整个GC堆,但是还是保留了分代的概念;G1能设置停顿时间
ParNew+CMS 与G1区别-----G1独立管理整个GC堆,但是还是保留了分代的概念;G1能设置停顿时间;G1多了mixedGC,基本不会fullGC

注:
ParNew原理---多线程进行复制算法
CMS原理---“并发-标记-清除”算法,
初始标记:暂停所有的其他线程,并记录下直接与root相连的对象,速度很快
并发标记:开启GC线程和用户线程,并行去记录可达对象,因为并行,所以可能漏掉可达对象
重新标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分漏掉的可达对象的标记记录
并发清除
回收:开启线程,并行去清扫标记的区域


G1原理---“并发-标记-整理”算法,
初始标记:暂停所有的其他线程,并记录下直接与root相连的对象,速度很快
并发标记:开启GC线程和用户线程,并行去记录可达对象,因为并行,所以可能漏掉可达对象

最终标记:修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分漏掉的可达对象的标记记录
筛选整理回收:开启线程,并行去清扫标记的区域,并且根据设置的GC停顿时间,优先选择回收价值最大的Region

JVM调优主要就是调整下面两个指标
停顿时间:垃圾收集器做垃圾回收中断应用执行的时间。-XX:MaxGCPauseMillis
吞吐量:非垃圾收集的时间和总时间的占比:1/(1+n),吞吐量为1-1/(1+n)。-XX:GCTimeRatio=n(GC停顿时间短,吞吐量就高)


GC调优步骤(调优至99%吞吐量)

1.开启打印GC日志
-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCDateStamps-Xloggc:./gc.log
Tomcat可以直接修改JAVA_OPTS变量
2.如果系统慢或报错,下载日志文件,一般我们用gceasy分析,会有一些建议性的提示会高效很多。
3.导入gceasy,查看分析日志得到关键性指标:比如吞吐量、GC停顿时间
3.分析GC原因,调优JVM参数,比如:
.OutOfMemoryError,见下面三种:
1.(堆)溢出,手动设大 JVM Heap(堆)的大小。
2.持久代(元空间)溢出,手动设大 MaxPermSize 大小
3.栈溢出,1MB 是足够的,一般256K就够了,要想着从代码层优化:不要递归的层次过多

.比如频繁fullGC:考虑增加老年代比例(调小新生代比例参数)
.还有几种如初始堆内存设到最大推内存一样,见下面一个问题JVM调优明细

?你平时是怎么进行tomcat调优的?(新生代使用并行收集器ParNew(无碎片,停顿不明显,吐量高),老年代使用并行标记整理收集器CMS )

一、Tomcat的自身调优
1.缓存优化,采用动静分离节约Tomcat 的性能:采用Nginx+Tomcat 实现动静分离,让 Tomcat 只负责 jsp 文件的解析工作,Nginx负责静态资源的访问(静态页面缓存到Nginx
2.调整 Tomcat 的线程池:比如线程池中最大的线程数量(默认值为150,改为400),线程池中允许空闲的线程数量等(空闲改成4个)
3.调整 Tomcat 的连接器:比如,指定Tomcat连接器所使用的执行器(线程池) 是我们前面配好的线程池;网络连接超时设置为20秒,设置不允许反查域名关闭客户端dns查询,提高处理能力
4.修改 Tomcat 的运行模式:APR运行模式,APR是从操作系统级别解决异步 IO 问题(提升 Tomcat 对静态文件的处理性能,当然也可以采用动静分离。)
5.禁用 AJP 连接器:使用 Nginx+Tomca t的架构,所以用不着 AJP 协议,所以把AJP连接器禁用。系统安装 Apr 库(性能最优),默认就启用的 Apr 协议,不然会使用 bio 方式
6.使用tomcat集群,配合nginx做集群
二、JVM的调优  (考虑1.停顿时长:2.吞吐量:3.频繁率)(默认Edem : from : to = 8 : 1 : 1,新生代 与老年代 为 1:2
Tomcat 是运行在 JVM 上的,所以对 JVM 的调优也是非常有必要的。(修改启动脚本catalina.sh加上:JAVA_OPTS='-server -Xms1024m -Xmx1024m...'
例如:假如4核8G,指定---新生代使用并行回收器ParNew(无碎片,停顿不明显),老年代使用并行标记整理回收器CMS (选型原因?如下)
1.最大可用推内存,从默认的 256M增大到4G  (减少GC次数,少停顿吞吐量就越高
2.初始堆内存设到最大推内存一样,  (避免收缩堆内存大小,影响效率,---防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间
3.-Xss 每个线程堆栈的大小 一般情况下256K是足够了,调小可以提升并发线程数(代码不要递归的层次过多)
4.设置持久代初始值和最大值一样。
5.大对象直接进入老年代(设置阀值)
6.设置对象进入老年代的年龄
7.吞吐量优先设置,(更像是G1里的,但老年代的CMS也有一个择中办法:兼顾停顿时间不能太长和空间碎片定次整理
8将新对象预留在新生代,
其他版本:

?三种JVM内存溢出怎么解决?

1.(堆)溢出,手动设大 JVM Heap(堆)的大小。
2.持久代(元空间)溢出,手动设大 MaxPermSize 大小
3.栈溢出,1MB 是足够的,一般256K就够了,要想着从代码层优化:不要递归的层次过多



 

调优总结

初始堆值和最大堆内存内存越大,吞吐量就越高
最好使用
并行收集器,因为并行收集器速度比串行吞吐量高,速度快
设置堆内存
新生代的比例和老年代的比例最好为1:2或者1:3
减少GC对老年代的回收。

 

GC是在什么时候,对什么东西,做了什么事情?

一、什么时候
发生GC分几种情况:
新生代minor gc----Eden区域满了,或者新创建的对象大小 > Eden所剩空间
新生代major gc----如下
新生代Full GC----新生代直接晋升到老年代的大对象超过了老年代的剩余空间或超过设定年龄生存次数,Major GC发生,由于minor 刚才也发生了,所以全回收一次,可以把此当成fullGC,  (System.gc()也可以,但一般会设置来禁止这么干)
Mixed GC----收集整个young gen以及部分old gen的GC。只有G1有这个模式

二、对什么东西 
从root搜索不到,而且经过第一次标记、清理后,仍然没有复活的对象。 
    分析:我期待的答案。但是的确很少面试者会回答到这一点,所以在我心中回答道第3点我就给全部分数。 
三、做了什么事情(直接讲原理就好)
新生代用多线程做了并行复制算法,通常用parnew,比如对象GC之后先放入from,下次GC时会把from里存活的全复制到to区,清空from,复制算法好处----无碎片,但牺牲一半空间。
老年代做的是并发标记-清除,通常用CMS,标记-清除算法好处--节省空间,但有碎片。 


调优:
第一次调优,设置Metaspace大小:增大元空间大小-XX:MetaspaceSize=64M-XX:MaxMetaspaceSize=64M
第二次调优,添加吞吐量和停顿时间参数:-XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=10
GC常用参数

堆栈设置
-Xss:每个线程的栈大小
-Xms:初始堆大小,默认物理内存的1/64
-Xmx:最大堆大小,默认物理内存的1/4
-Xmn:新生代大小
-XX:NewSize:设置新生代初始大小
-XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。
-XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。
-XX:MetaspaceSize:设置元空间大小
-XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
收集器设置
-XX:+UseParNewGC:在新生代使用并行收集器
并行收集器设置
-XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis:设置并行收集最大暂停时间
-XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
CMS收集器设置
-XX:+UseConcMarkSweepGC:设置CMS并发收集器
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
G1收集器设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区
-XX:GCTimeRatio:吞吐量大小,0-100的整数(默认9),值为n则系统将花费不超过1/(1+n)的时间用于垃圾收集
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor填充容量(默认50%)
-XX:MaxTenuringThreshold:最大任期阈值(默认15)
-XX:InitiatingHeapOccupancyPercen:老年代占用空间超过整堆比IHOP阈值(默认45%),超过则执行混合收集
-XX:G1HeapWastePercent:堆废物百分比(默认5%)
-XX:G1MixedGCCountTarget:参数混合周期的最大总次数(默认8)

-------------------Old-------------  

巧记:

我的记忆方式如下:
近身类
1)-Xms*M          ,ms看做的memory size(初始堆内存大小)的缩写,就是初始堆大小
2)-Xmx*M         , Maximum heap memory(最大堆内存),x代表最大,所以就最大堆内存
3)-Xss*K         , ss就是stack size(栈大小)的缩写,所以是用来代表线程栈的大小
4)-Xmn*M           ,n代表是memory new(内存新生代/新生代堆最大可用值),所以mn就是用来指定新生代的堆内存空间大小

等于符号类
1)-XX:PermSize=*M        设置Persistent Size(持久区大小),在jdk 8中已经被metaspace取代
2)-XX:MaxPermSize=*M          设置Maximum Persistent Size(最大持久区大小),在jdk 8中已经被metaspace取代
3)-XX:SurvivorRatio=1:1       用来设置新生代中eden空间和from(或to)空间的比例.
固定开关:
-XX:+PrintGC         每次触发GC的时候打印相关日志
-XX:+UseSerialGC         串行回收
-XX:+PrintGCDetails      更详细的GC日志


?说说JVM内存模型及作用?

一、---------五大内存模块---------(1.8版本以前方法区是在堆里面的,所以以前学的常量那些都在堆里---1.8多出来一个方法区,把常量等从堆里分出来)
方法区:常量、静态变量、类信息、Jit (生效时间:随JVM启动程序开始运行加载所有类信息开始,失效时间:JVM程序停止运行)--(原空间)
堆(大范围):存对象的实例(实例里包含有成员变量) (生效时间:随类被New创建实例信息开始,失效时间:实例外)--还包含方法里new的实例本身(对象引用是局部变量)
栈(小范围临时客栈):普通方法及内部局部变量(生效时间:随方法被调用开始,失效时间:方法外)
本地方法栈:native修饰的方法, Java 通过调用这个接口从而调用到 C/C++ 方法
程序计数器: 是用于存放下一条指令所在单元的地址的地方。---存储 每个指 令单元的地址,线程阻塞恢复运行时,能立马通过当前这个线程存在于程序计数器的指令地址,找到对应的字节码指令继续执行

?说说对垃圾回收机制的了解

     什么是拉圾回收:一个内存对象的生命周期超出了程序需要它的时间长度,也就是说没有人引用它了,它还“游离”在内存中,会浪费资源,我们需要对它清理就是拉圾回收。
    垃圾回收简要过程总结:所有没有再被引用的对象,都会被第一次标记;如果重写了finalize(),会被放入F-Query队列,然后执行finalize(),执行finalize()时重新与GC Roots引用链恢复连接的对象,会获得唯一一次机会恢复成正常的对象,其他不满足条件的就会在本次回收掉。
   
结合对象的生命周期说一下吧, 拉圾回收机制会根据生命周期长短不一样,把对象放进不同的内存区.
第一步,首先一个对象,会在JVM内存的eden区被创建,分配内存区域,eden空间100%满后,无法分配内存,会触发GC回收。
第二步,gc回收时,保留一部分剩余存活对象存入survivor(假设to区),同时,利用copy复制算法,把from的剩余存活对象也转到to区,始终保持一个(from=s0)survivor为empty。并为转移对象age+1.
第三步,之后一直循环上述步骤,当age满足很大的时候触发老年代gc回收保存到old 老年代区。(老年这里还不是full gc,还没满),如果超过了设置的放入老年代的对象大小阀值,也会直接进入老年代。
第四步,由于不断会有对象进入老年代,老年代内存会一直增大加上新生代达到临界值内存最大值。触发full gc进行整体回收。

疑问点:那你没有说明元空间啊?元空间干啥的呢?
元空间存放:class文件、静态对象、属性等。而且在永久代的时候默认大小256m,元空间是可以动态拓展的

JVM调优主要就是调整下面两个指标

停顿时间:  垃圾收集器做垃圾回收中断应用执行的时间。  -XX:MaxGCPauseMillis
吞吐量:垃圾收集时间(如果太频繁,非停顿的普通gc也会占了cpu线程资源多)和总时间的占比:1/(1+n),吞吐量为1-1/(1+n) 。   -XX:GCTimeRatio=n

?年轻代和老年代分别选择什么选择器,分别介绍一下优点?

新生代使用并行收集器ParNew(无碎片,停顿不明显,吐量高),老年代使用并行标记整理收集器CMS
一、并行回收器:在串行回收器基础上做了改进,他可以使用多个线程同时进行垃
圾回收,基于复制算法,无碎片,停顿不明显

二、标记整理回收器:整个收集过程大致分为4个步骤:
①.初始标记(CMS initial mark)  标记出GC ROOTS能直接关联到的对象  ,停顿时间一般
②.并发标记(CMS concurrenr mark) 进行GC ROOTS 根搜索算法阶段(可达性分析),,会判定对象是否存活,不停顿,耗时长
③.重新标记(CMS remark) 修正并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录  ,停顿时间稍长
.并发清除(CMS concurrent sweep) 并发清除.,不停顿,耗时长
    老年代使用并发标记清理收集器CMS选型原因:CMS垃圾收集器提供了一个可配置的参数,即-XX:+UseCMSCompactAtFullCollection开关参数,用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程(会造成停顿时间增长),空间碎片问题没有了,但停顿时间不得不变长了,JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的(择中的办法:兼顾停顿时间不能太长和空间碎片定次整理

-------------GC算法-------------

---复制---
---标记-清除---
---标记-整理算法---

让所有存活的对象向一段移动,然后直接清理掉端边界以外的内存
---分代收集算法---
新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集
---G1---

-------------------类加载、字节码相关-------------

?类的生命周期?

巧记:加验,准解,初使,卸

加载、验证、准备、解析、初始化、使用、卸载

 

Java 类加载过程?(JVM加载class文件的原理机制)

类的加载指的是将类的.class文件中的二进制数据,通过类加载器读入到内存中,将其数据结构放在方法区内,然后在堆区创建一个java.lang.Class对象,,并且向Java程序员提供了访问方法区内的数据结构的接口

启动类加载器有三种?

巧记:启动扩展应用

启动类加载器 Bootstrap ClassLoader  ----加载JDK安装目录\jre\lib下的类库
扩展类加载器  Extension ClassLoader  ----加载JDK安装目录\jre\lib\ext下的类库
应用程序加载器(系统类加载器)Application ClassLoader  ----加载用户类路径(ClassPath)所指定的类

附:用户自定义加载器(User ClassLoader):负责加载用户自定义路径下的类包

双亲委派模型是什么?怎么破坏双亲委派模型?

双亲委派机制是:一个类加载器接收到加载类的请求,它首先不会自己加载,而是把请求委派给父加载器加载,每一个层次的加载器都如此,因此,所有的加载请求都应该传送到顶层的父类加载器,只有当父类加载器反馈自己无法完成加载请求时,子类加载器才会尝试自己去加载。

怎么破坏双亲委派模型:模块热部署

 

----------类加载器怎么实现热部署----------

https://blog.csdn.net/chachapaofan/article/details/88697452

深层原理---------使用两个ClassLoader一个Classloader加载那些不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为restart ClassLoader,这样,有代码更改的时候,原来加载的类会失效,重新加载新的这些代码,由于需要加载的类相比较少,所以实现了较快的热部署/热加载,不用重启

 

Java对象创建过程


1.JVM遇到一条新建对象的指令时首先去检查这个指令的参数是否能在常量池中定义到一个类的符号引用。然后加载这个类(类加载过程在后边讲)
2.为对象分配内存。一种办法“指针碰撞”、一种办法“空闲列表”,最终常用的办法“本地线程缓冲分配(TLAB)”
3.将除对象头外的对象内存空间初始化为0
4.对对象头进行必要设置

 

 

-------------类加载机制-------------

全盘负责委托机制
当类加载器加载一个类的时候,该类所依赖和引用的类也由这个
类加载器自已载入
(除了显示的使用另一个ClassLoader外)

双亲委派机制
双亲委派机制得工作过程:
1-类加载器收到类加载的请求;
2-把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器;
3-启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);不能加载,则抛出异常,通知子加载器进行加载。
4-重复步骤三; 
以上就是双亲委派机制的原理。

双亲委托模式的弊端


判断类是否加载的时候,应用类加载器会顺着双亲路径往上判断,直到启动类加载器.但是启动类加载器不会往下询问,这个委托路线是单向的,即顶层的类加载器,无法访问底层的类加载器所加载的类

这样就出现了一个问题:如何在父加载器加载的类中,去调用子加载器去加载类?


jdk提供了两种方式,Thread.currentThread().getContextClassLoader()和ClassLoader.getSystemClassLoader()一般都指向AppClassLoader,他们能加载classpath中的类
SPI则用Thread.currentThread().getContextClassLoader()来加载实现类,实现在核心包里的基础类调用用户代码


打破双亲委派机制

SPI
从META-INF/services/java.sql.Driver文件得到实现类名字DriverA
Class.forName("xx.xx.DriverA")来加载实现类----如Jdbc,本来是用启动类加载器去加载spi配的driver,但Meta_info在非rt.jar包里,它找不到,只能让下层的孙子应用类加载器(它才能找到这个目录)去加载。
Class.forName()方法默认使用当前类的ClassLoader,JDBC是在DriverManager类里调用Driver的,当前类也就是DriverManager,它的加载器是BootstrapClassLoader。
用BootstrapClassLoader去加载非rt.jar包里的类xx.xx.DriverA,就会找不到
要加载xx.xx.DriverA需要用到AppClassLoader或其他自定义ClassLoader
最终矛盾出现在,要在BootstrapClassLoader加载的类里,调用AppClassLoader去加载实现类

 

 

-------------------需要复习的题-------------


-----
21.调优命令
Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo

jps,JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstat,JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。
jmap,JVM Memory Map命令用于生成heap dump文件
jhat,JVM Heap Analysis Tool命令是与jmap搭配使用,用来分析jmap生成的dump,jhat内置了一个微型的HTTP/HTML服务器,生成dump的分析结果后,可以在浏览器中查看
jstack,用于生成java虚拟机当前时刻的线程快照。
jinfo,JVM Configuration info 这个命令作用是实时查看和调整虚拟机运行参数。
-----
22.调优工具
常用调优工具分为两类,jdk自带监控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

jconsole,对JVM中内存,线程和类等的监控
jvisualvm(装visualGC+jconsole),jdk自带全能工具,分析程序死锁,(监视标签里还能导出正在监控的)内存dump、线程dump;(用它的VisualGc插件来)监控内存变化、GC变化等。还能导入生产的堆dump文件(dump文件里能看到类实例对象的大小占比找出大对象)
GCeasy,一款专业分析gc日志的工具,能查内存大小分配比例,吞吐量,最大最小停顿时间,普通GC和fullGC的GC耗时时长和,查找内存泄漏
-----
2.JVM的主要组成部分及其作用?

类加载器 ClassLoader:Java代码 -----> 字节码 的编译过程

运行时数据区:把上一步编译得到的字节码加载到内存中

执行引擎:命令解析器,解析上一步加载而来的字节码,翻译成为系统指令,交由CPU执行

本地库接口 Native Interface:诸如IO之类的由其他语言写成的本地库接口
-----
3.JVM运行时数据区包含哪些?

程序计数器:行号指示器,通过改变该值,以选取下一步的指令

Java虚拟机栈:局部变量、方法出口等,为JVM服务

本地方法栈:局部变量、方法出口等,为本地Native方法服务

堆区:内存最大的一块,所有的对象实例都在这里分配内存

方法区:常量、静态变量等
-------
8.堆栈的区别?

栈内存存储的是局部变量,堆内存存储的是实体;

栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;

栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。(所以拉圾回收只回收堆和方法区)
-------
JVM内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作 内存等。
内存屏障:为了保障执行顺序和可见性的一条cpu指令 
重排序:为了提高性能,编译器和处理器会对执行进行重拍 
happen-before:操作间执行的顺序关系。有些操作先发生。 
主内存:共享变量存储的区域即是主内存 
工作内存:每个线程copy的本地内存,存储了该线程以读/写共享变量的副本 
-------
讲讲JAVA的反射机制。
Java程序在运行状态可以动态的获取类的所有属性和方法,并实例化该类,调用方法的功能 

-------

-------

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java_爱吃肉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值