GC概念、收集方法
有关垃圾收集器的常见问题及解答

JVM面试总结

Java面试题--JVM 底层 与 GC(Garbage Collection)

1.GC是什么?为什么要有GC
2.什么时候会导致垃圾回收
3.GC是怎么样运行的
4.新老以及永久区是什么
5.GC 有几种方式?怎么配置
6.什么时候一个对象会被GC? 如何判断一个对象是否存活
7.System.gc() Runtime.gc()会做什么事情? 能保证 GC 执行吗
8.垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
9.Minor GC 、Major GC、Young GC 与 Full GC分别在什么时候发生
10.垃圾回收算法的实现原理
11.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
12.垃圾回收的最佳做法是什么
13.GC收集器有哪些
14.串行(serial)收集器和吞吐量(throughput)收集器的区别是什么
15.CMS垃圾回收器的工作过程
16.JVM 中一次完整的 GC 流程是怎样的? 对象如何晋升到老年代
17.吞吐量优先和响应优先的垃圾收集器选择
18.举个实际的场景,选择一个GC策略
19.JVM的永久代中会发生垃圾回收吗
20.标记清除、标记整理、复制算法的原理与特点?分别用在什么地方
21.如果让你优化收集方法,有什么思路
22.说说你知道的几种主要的jvm 参数
23.-XX:+UseCompressedOops 有什么作用
24.Java 类加载器都有哪些
25.JVM如何加载字节码文件
26.JVM内存分哪几个区,每个区的作用是什么
27.一个对象从创建到销毁都是怎么在这些部分里存活和转移的(Java中对象的创建、内存分配和销毁)
28.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法
29.JVM中哪个参数是用来控制线程的栈堆栈小
30.简述内存分配与回收策略
31.简述重排序,内存屏障,happen-before,主内存,工作内存
32.Java中存在内存泄漏问题吗?请举例说明
33.简述 Java 中软引用(SoftReferenc)、弱引用(WeakReference)和虚引用
34.内存映射缓存区是什么
35.jstack,jstat,jmap,jconsole怎么用
36.32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?32 位和 64 位的 JVM,int 类型变量的长度是多数?
37.怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位
38.JVM自身会维护缓存吗?是不是在堆中进行对象分配,操作系统的堆还是JVM自己管理堆
39.什么情况下会发生栈内存溢出
40.双亲委派模型是什么


1.GC是什么?为什么要有GC
GC:Java的垃圾回收器。
Java是由C++发展来的。它摈弃了C++中一些繁琐容易出错的东西。其中有一条就是这个GC。
写C/C++程序,程序员定义了一个变量,就是在内存中开辟了一段相应的空间来存值。内存再大也是有限的,所以当程序不再需要使用某个变量的时候,就需要释放这个内存空间资源,好让别的变量来用它。在C/C++中,释放无用变量内存空间的事情要由程序员自己来解决。就是说当程序员认为变量没用了,就应当写一条代码,释放它占用的内存。这样才能最大程度地避免内存泄露和资源浪费。但是这样显然是非常繁琐的。程序比较大,变量多的时候往往程序员就忘记释放内存或者在不该释放的时候释放内存了。而且释放内存这种事情,从开发角度说,不应当是程序员所应当关注的。程序员所要做的应该是实现所需要的程序功能,而不是耗费大量精力在内存的分配释放上。
Java有了GC,就不需要程序员去人工释放内存空间。当Java虚拟机发觉内存资源紧张的时候,就会自动地去清理无用变量所占用的内存空间。当然,如果需要,程序员可以在Java程序中显式地使用System.gc()来强制进行一次立即的内存清理。
2.什么时候会导致垃圾回收
在新生代的Eden区满了,会触发新生代GC(Mimor GC),经过多次触发新生代GC存活下来的对象就会升级到老年代,升级到老年代的对象所需的内存大于老年代剩余的内存,则会触发老年代GC(Full GC)。当程序调用System.gc()时也会触发Full GC。
年老代(Tenured)被写满
持久代(Perm)被写满
System.gc()被显示调用
上一次GC之后Heap的各域分配策略动态变化
3.GC是怎么样运行的
Java的内存管理实际就是对象的管理,其中包括对像的分配和释放。对于程序员来说,分配对象使用new关键字,释放对象时只是将对象赋值为null,让程序员不能够再访问到这个对象,该对象被称为“不可达”。GC将负责回收所有“不可达”对象的内存空间。
对于GC来说,当程序员创建对象时,GC就开始监控这个对象地址、大小以及使用情况。通常GC采用有向图的方式记录并管理堆中的所有对象,通过这种方式确定哪些对象是“可达”的,哪些对象是“不可达”的。当GC确定一些对象为“不可达时”GC就有责任回收这些内存空间,但为了GC能够在不同的平台上实现,java规范对GC的很多行为都没有进行严格的规定。例如对于采用什么类型的回收算法、什么时候进行回收等重要问题都没有明确的规定,因此不同的JVM实现着不同的的实现算法,这也给JAVA程序员的开发带来了很多不确定性。
4.新老以及永久区是什么
根据存活的生命周期将内存分为若干个区:新生代和老年代和永久代
(1)新生代:储存所有生成的对象,每次都回收大量对象——>Copying复制法
(2)老年代:新生代中的对象经过几次gc之后,没有被回收,进入老年代中,每次都回收少量对象——>Mark-Compact法
(3)永久代:存储class类,常量,从配置的角度看,这个域是独立的,不包括在JVM堆内。默认为4M。方法描述:回收废弃常量和无用类
有的虚拟机并没有永久代,JAVA8 开始永久代也已经被彻底删除了,取代它的是另一个内存区域也被称为 元空间。
5.GC 有几种方式?怎么配置(答案感觉不太对)
①引用计数
对象增加一个引用时,引用数+1。减少一个时,引用数-1。当进行垃圾回收时,只回收引用数为0的对象。面对互相引用无解,方法比较老,基本弃用
②标记+清除
从根节点遍历标记对象,然后遍历整个堆,清除没有标记的对象。
缺点:运行效率不高,产生了内存碎片,运行时需要暂停应用
③复制
将内存分为2个区域(区域a和区域b),一个区域空着(区域a),另一个区域(区域b)放对象。垃圾回收时,将区域b还在引用的对象复制到空着的区域(区域a)中去,并且当前区域(区域b)清空。如此反复
缺点:复制到a区域的时候,同时进行了内存整理,没有内存碎片。但是需要2倍的内存
④标记+整理
从根节点遍历标记对象,然后遍历整个堆,清除没有标记的对象,并且将有标记的对象压缩到一块的区域内
特点:没有内存碎片,也不需要2倍内存
6.什么时候一个对象会被GC? 如何判断一个对象是否存活
(1)引用计数算法(已被淘汰的算法)
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
目前主流的java虚拟机都摒弃掉了这种算法,最主要的原因是它很难解决对象
之间相互循环引用的问题。尽管该算法执行效率很高。
(2)可达性分析算法
目前主流的编程语言(java,C#等)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的
在Java语言中, 可作为GC Roots的对象包括下面几种:
虚拟机栈中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI[即一般说的Native]引用的对象
判定一个类是否是“无用的类”的条件苛刻许多。需要同时满足下面的三个条件:
该类的所有实例已经被回收,也就是Java堆中不存在该类的任何实例
加载该类的ClassLoader已经被回收
该类对应的class对象已经被回收,无法在任何地方通过反射访问到该类的方法
注意finalize()方法中重新使某个引用指向该对象,该对象可以被"复活"
7.System.gc() Runtime.gc()会做什么事情? 能保证 GC 执行吗
java.lang.System.gc()只是java.lang.Runtime.getRuntime().gc()的简写,两者的行为没有任何不同。
这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。
不能。
8.垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
可以。程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。
9.Minor GC 、Major GC、Young GC 与 Full GC分别在什么时候发生
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为Minor GC
当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,
取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。
其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。
Major GC 是清理永久代。
Full GC 是清理整个堆空间—包括年轻代和永久代。   
 

Minor GC
:当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁
young GC:当young gen中的eden区分配满的时候触发。注意young GC中有部分存活对象会晋升到old gen,所以young GC后old gen的占用量通常会有所升高。
Major GC/full GC:当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC(因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先触发一次单独的young GC);或者,如果有perm gen的话,要在perm gen分配空间但已经没有足够空间时,也要触发一次full GC;或者System.gc()、heap dump带GC,默认也是触发full GC。
10.垃圾回收算法的实现原理
用户Java程序运行过程中,Java虚拟机提供了另外一个系统级的线程,专门负责回收不再被使用的对象占用的内存,这一过程称为垃圾回收。垃圾回收需要对堆内存中的对象进行标记,并对堆内存进行整理。这一过程的某些阶段需要暂时终止用户Java线程,等回收工作完成后再恢复执行。因此,频繁地触发虚拟机垃圾回收操作的行为会影响程序的运行效率。
11.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?
垃圾收集器不会立即释放对象占用的内存,在下一个垃圾回收周期中,这个对象将是可被回收的。         
12.垃圾回收的最佳做法是什么
1、标记-清除算法:
    首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。标记过程中 实际上即时上面说的finaLize()的过程。主要缺点一个是效率问题。另外一个是空间问题,标记清除后会产生大量不连续的内存碎片。
2、复制算法:
   这种算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了。就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉。
3、标记-整理算法:
    复制收集算法在对象存活率较高时就要执行较多的复制操作,效率将会遍低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以对应被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
标记过程仍然与标记-清除算法一样,但是后续步骤不是直接将对可回收对象进行清理,而是让所有存活的对象都向领一端移动,然后直接清理掉端边界以外的内存。
4、分代收集算法:
  当代商业虚拟机的垃圾收集都采用的是“分代收集算法” ,根据对象的存活周期的不同,将内存化为几块,一般是把java堆分为新生代和老年代。这样就可以根据各个年代的特点采用最合适的收集算法。
新生代选用复制算法,老年代使用标记-清理算法 或者 标记-整理算法。
13.GC收集器有哪些 参考链接
1、serial收集器
serial是一个单线程的垃圾收集器,它只会使用一个cpu、一个收集线程工作;它在进行gc的时候,必须暂停其他所有线程到它工作结束(这种暂停往往让人难以接受)。
对于单个cpu的情况下,serial是没有线程交互的开销,效率要好于其他。例如在一个简单的桌面应用,只有几十M的内存,他的停顿时间可能只有几十毫秒,所以一般默认的client模式下都是用的serial做默认垃圾收集器。

2、parnew收集器
parnew其实就是serial的多线程版本,parnew在单线程的情况下甚至不如serial,parnew是除了serial之外唯一能和CMS配合的。
parnew默认开启收集线程数和cpu的数量相同,我们可以利用-XX:ParallelGCThreads参数来控制它开启的收集线程数。

3、parallel scavenge收集器
parallel scavenge主要就是关注吞吐量。
所谓吞吐量:运行用户代码的世界/(运行用户代码时间+GC花费的时间)。
parallel scavenge收集器中,提供了2个参数来控制吞吐量:
-XX:GCTimeRatio:gc时间占用的总比例,也就是吞吐量的倒数。
-XX:MaxGCPauseMillis:最大的暂停毫秒数(这个数值并非越小越好,如果把他设置小了,系统会根据这个值调整空间的大小,也就会加快GC的频率)
parallel scavenge可以设置开启-XX:UseAdaptiveSizePolicy,开启这个参数之后就无需关注新生代大小eden和survivor等比例,晋升老年代对象年龄的这些细节了。

4、serial old收集器
serial收集器的老年代版本,使用标记整理算法,主要有两个作用:
jdk5之前和parallel scavenge配合使用
作为cms失败的后备收集方案

5、parallel old收集器
是parallel收集器的老年代版本,用于和parallel收集器搭配组合的,因为parallel收集器不能和cms组合,但是和serial old收集器效率又太低。
对吞吐量和CPU敏感度特别关注的应用可以使用parallel+parallel old的组合。

6、CMS收集器、特点、过程、缺点
CMS的适用特点
希望JAVA垃圾回收器回收垃圾的时间尽可能短;
应用运行在多CPU的机器上,有足够的CPU资源;
有比较多生命周期长的对象;
希望应用的响应时间短。
CMS的过程
初始标记:标记一下GC ROOT能直接关联的对象,速度很快,这个阶段是会STW。
并发标记:GC ROOT的引用链的查找过程,标记能关联到GC ROOT的对象,这一个阶段是不需要STW的。
重新标记:在并发标记阶段,应用的线程可能产生新的垃圾,所以需要重新标记,这个阶段也是会STW。
并发清除:这个阶段就是真正的回收垃圾的对象的阶段,无需STW。
CMS的缺点
对cpu比较敏感。
可能出现浮动垃圾:在并发清除阶段,用户还是继续使用的,这时候就会有新的垃圾出现,CMS只能等下一次GC才能清除掉他们。
CMS运行期间预留内存不够的话,就会出现concurrent Mode Failure,这时候就会启动serial收集器进行收集。
CMS基于标记清除算法实现,会产生内存碎片空间。碎片空间过多就会对大对象的分配空间造成麻烦。为了解决碎片问题,CMS提供一个参数来控制是否在GC的时候整合内存中的碎片,这个碎片整合的操作是无法并发的,会延长STW的时间。

7、G1收集器
G1的特点
利用多CPU来缩短STW的时间
可以独立管理整个堆(使用分代算法)
整体是基于标记-整理,局部使用复制算法,不会产生碎片空间
可以预测停顿:G1吧整个堆分成多个Region,然后计算每个Region里面的垃圾大小(根据回收所获得的空间大小和回收所需要的时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。
G1的运行过程
初始标记:标记一下GC ROOT能直接关联的对象,速度很快,这个阶段是会STW。
并发标记:在GC ROOT中运用可达性分析算法,找出存活的对象,耗时较长,但是无需STW。
最终标记:修正并发标记期间用户线程对垃圾对象的修改,需要停顿线程,但是可以并行执行。
筛选回收:先计算回收各个Region的价值,然后根据用户需求来进行回收。
14.串行(serial)收集器和吞吐量(throughput)收集器的区别是什么
串行GC:整个扫描和复制过程均采用单线程的方式,相对于吞吐量GC来说简单;适合于单CPU、客户端级别。
吞吐量GC:采用多线程的方式来完成垃圾收集;适合于吞吐量要求较高的场合,比较适合中等和大规模的应用程序。
吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。
15.CMS垃圾回收器的工作过程
初始标记:标记一下GC ROOT能直接关联的对象,速度很快,这个阶段是会STW。
并发标记:GC ROOT的引用链的查找过程,标记能关联到GC ROOT的对象,这一个阶段是不需要STW的。
重新标记:在并发标记阶段,应用的线程可能产生新的垃圾,所以需要重新标记,这个阶段也是会STW。
并发清除:这个阶段就是真正的回收垃圾的对象的阶段,无需STW。
16.JVM 中一次完整的 GC 流程是怎样的? 对象如何晋升到老年代 参考链接
7.GC概念、收集方法
(1).当现在有一个新的对象产生,那么对象一定需要内存空间,于是现在就需要为该对象进行内存空间的申请;
(2).首先会判断伊甸园区是否有内存空间,如果此时有内存空间,则直接将新对象保存在伊甸园区;
(3).但是如果此时伊甸园区的内存空间不足,那么会自动执行一个MinorGC操作,将伊甸园区的无用内存空间进行清理,清理之后会继续判断伊甸园区的内存空间是否充足?如果内存空间充足,则将新的对象直接在伊甸园区进行空间分配;
(4).如果执行了Minor GC之后发现伊甸园区的内存依然不足,那么这个时候会进行存活区判断,如果存活区有剩余空间,则将伊甸园区的部分活跃对象保存在存活区,那么随后继续判断伊甸园区的内存空间是否充足,如果充足,则在伊甸园区进行新对象的空间分配;
(5).如果此时存活区也已经没有内存空间了,则继续判断老年区,如果此时老年区空间充足,则将存活区中的活跃对象保存到老年代,而后存活区就会存现有空余空间,随后伊甸园区将活跃对象保存在存活区之中,而后在伊甸园区里为新对象开辟空间;
(6).如果这个时候老年代也满了,那么这个时候将产生M ajor GC(FullGC),进行老年代的内存清理。
(7).如果老年代执行了Full GC之后发现依然无法进行对象的保存,就会产生OOM异常“OutOfMemoryError”

存活对象会不断地在两个存活区之间来回地复制,直到其中的一些对象被认为是已经成熟,足够老了。
在一轮GC完成后,每个分区中存活下来的对象的计数便会增加(如果刚刚从Eden存活下来其年龄=1),
当一个对象的年龄超过了一个特定的年老阈值之后,它便会被提升到老年代中。
出现对象提升的时候,这些对象则不会再被复制到另一个存活区,而是直接复制到老年代中。
如果存活区的大小不足以存放所有的新生代存活对象,则会出现过早提升。
17.吞吐量优先和响应优先的垃圾收集器选择
吞吐量优先的并行收集器:以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
响应时间优先的并发收集器:保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
18.举个实际的场景,选择一个GC策略

19.JVM的永久代中会发生垃圾回收吗
垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。
Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区
20.标记清除、标记整理、复制算法的原理与特点?分别用在什么地方
1:标记清除:直接将要回收的对象标记,发送gc的时候直接回收:特点回收特别快,但是回收以后会造成很多不连续的内存空间,因此适合在老年代进行回收,CMS(current mark-sweep),就是采用这种方法来会后老年代的。
2:标记整理:就是将要回收的对象移动到一端,然后再进行回收,特点:回收以后的空间连续,缺点:整理要花一定的时间,适合老年代进行会后,parallel Old(针对parallel scanvange gc的) gc和Serial old就是采用该算法进行回收的。
3:复制算法:将内存划分成原始的是相等的两部分,每次只使用一部分,这部分用完了,就将还存活的对象复制到另一块内存,将要回收的内存全部清除。这样只要进行少量的赋值就能够完成收集。比较适合很多对象的回收,同时还有老年代对其进行担保。(serial new和parallel new和parallel scanvage)
21.如果让你优化收集方法,有什么思路

对复制算法的优化:并不是将两块内存分配同等大小,可以将存活率低的区域大一些,而让回收后存活的对象所占的区域小一些,不够的内存由老年代的内存来保证,这样复制算法的空闲的空间减少了。

两个survival区域的是为了减少风险率,有一个survivor区要参与回收,也要参与存储,只要只有10%的空间浪费,同时也减少对老年代的依赖。

22.说说你知道的几种主要的jvm 参数
1、-XX:+DisableExplicitGC
这个将会忽略手动调用GC的代码使得 System.gc()的调用就会变成一个空调用,完全不会触发任何GC。最主要的原因是为了防止某些手贱的同学在代码里到处写System.gc()的调用而干扰了程序的正常运行吧。有些应用程序本来可能正常跑一天也不会出一次full GC,但就是因为有人在代码里调用了System.gc()而不得不间歇性被暂停。使用这个可能会抛出这个异常:java.lang.OutOfMemoryError: Direct buffer memory  
2、-XX:+UseConcMarkSweepGC
设置并发收集,启用CMS策略。测试中配置这个以后,-XX:NewRatio=4的配置失效了。所以,此时年轻代大小最好用-Xmn设置。
3、-XX:+CMSClassUnloadingEnabled
打开永久带垃圾回收
4、-XX:+CMSPermGenSweepingEnabled
这个参数表示在使用CMS垃圾回收机制的时候是否启用类卸载功能。默认这个是设置为不启用的,所以你想启用这个功能你需要在Java参数中明确的设置下面的参数:-XX:+CMSClassUnloadingEnabled。 如果你启用了CMSClassUnloadingEnabled ,垃圾回收会清理持久代,移除不再使用的classes。这个参数只有在-XX:+UseConcMarkSweepGC 也启用的情况下才有用。一般都是:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled  -XX:+CMSPermGenSweepingEnabled 这三个同时使用。可以防止 java.lang.OutOfMemoryError: PermGen space
5、-XX:CMSInitiatingOccupancyFraction=80
默认CMS是在tenured generation沾满68%的时候开始进行CMS收集,如果你的年老代增长不是那么快,并且希望降低CMS次数的话,可以适当调高此值
6、-XX:+CMSParallelRemarkEnabled
为了减少第二次暂停的时间,开启并行remark
7、-XX:+UseCMSCompactAtFullCollection
由于CMS收集器会产生碎片,此参数设置在垃圾收集器后进行一次内存碎片整理,使用并发收集器时,开启对年老代的压缩.
8、-XX:+UseFastAccessorMethods
原始类型的快速优化
9、-XX:+UseCMSInitiatingOccupancyOnly
指定HotSpot VM总是使用-XX:CMSInitiatingOccupancyFraction的值作为old的空间使用率限制来启动CMS垃圾回收。如果没有使用-XX:+UseCMSInitiatingOccupancyOnly,那么HotSpot VM只是利用这个值来启动第一次CMS垃圾回收,后面都是使用HotSpot VM自动计算出来的值
10、配置jconsle监控参数:
-server -Dcom.sun.management.jmxremote.port=8903 (自定义端口)
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.rmi.server.hostname=192.168.10.38 (监控的服务器IP)
23.-XX:+UseCompressedOops 有什么作用
使用压缩指针,对64位VM有效,可以提高效率,但-Xmx不能大于32g

当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍。这也会对 CPU 缓存(容量比内存小很多)的数据产生不利的影响。因为,迁移到 64 位的 JVM 主要动机在于可以指定最大堆大小,通过压缩 OOP 可以节省一定的内存。通过 -XX:+UseCompressedOops 选项,JVM 会使用 32 位的 OOP,而不是 64 位的 OOP。

此选项允许在64位JVM中引用32位,并访问接近32 GB的堆。 (超过32位指针可以)(您也可以有近无限的关闭堆内存)。 这可以节省大量的内存,并可能提高性能。
在Java 8中,可以选择设置-XX:ObjectAlignmentInBytes= ,实际上如果堆大小为64 GB,那么将使用-XX:ObjectAlignmentInBytes=16并仍然使用32位引用。

24.Java 类加载器都有哪些
1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
3)App ClassLoader
负责记载classpath中指定的jar包及目录中class
4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader
加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。
25.JVM如何加载字节码文件
JVM主要完成三件事:
1、通过一个类的全限定名(包名与类名)来获取定义此类的二进制字节流(Class文件)。而获取的方式,可以通过jar包、war包、网络中获取、JSP文件生成等方式。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。这里只是转化了数据结构,并未合并数据。(方法区就是用来存放已被加载的类信息,常量,静态变量,编译后的代码的运行时内存区域)
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。这个Class对象并没有规定是在Java堆内存中,它比较特殊,虽为对象,但存放在方法区中。
26.JVM内存分哪几个区,每个区的作用是什么 参考链接
程序计数器(Program Counter Register)
       程序计数器是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指令。
  每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。
  如果程序执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令地址;如果正在执行的是一个本地(native,由C语言编写完成)方法,则计数器的值为Undefined,由于程序计数器只是记录当前指令地址,所以不存在内存溢出的情况,因此,程序计数器也是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。
虚拟机栈(JVM Stack)
       一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。
  局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,只有long和double类型会占用2个局部变量空间(Slot,对于32位机器,一个Slot就是32个bit),其它都是1个Slot。需要注意的是,局部变量表是在编译时就已经确定好的,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。
  虚拟机栈中定义了两种异常,如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError(栈溢出);不过多数Java虚拟机都允许动态扩展虚拟机栈的大小(有少部分是固定长度的),所以线程可以一直申请栈,直到内存不足,此时,会抛出OutOfMemoryError(内存溢出)。
  每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。
本地方法栈(Native Method Statck)
       本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的,在很多虚拟机中(如Sun的JDK默认的HotSpot虚拟机),会将本地方法栈与虚拟机栈放在一起使用。
  本地方法栈也是线程私有的。
堆区(Heap)
       堆区是理解Java GC机制最重要的区域,没有之一。在JVM所管理的内存中,堆区是最大的一块,堆区也是Java GC机制所管理的主要内存区域,堆区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对的,也有栈上直接分配的)。
  一般的,根据Java虚拟机规范规定,堆内存需要在逻辑上是连续的(在物理上不需要),在实现时,可以是固定大小的,也可以是可扩展的,目前主流的虚拟机都是可扩展的。如果在执行垃圾回收之后,仍没有足够的内存分配,也不能再扩展,将会抛出OutOfMemoryError:Java heap space异常。
  关于堆区的内容还有很多,将在下节“Java内存分配机制”中详细介绍。
方法区(Method Area)
       在Java虚拟机规范中,将方法区作为堆的一个逻辑部分来对待,但事实上,方法区并不是堆(Non-Heap);另外,不少人的博客中,将Java GC的分代收集机制分为3个代:青年代,老年代,永久代,这些作者将方法区定义为“永久代”,这是因为,对于之前的HotSpot Java虚拟机的实现方式中,将分代收集的思想扩展到了方法区,并将方法区设计成了永久代。不过,除HotSpot之外的多数虚拟机,并不将方法区当做永久代,HotSpot本身,也计划取消永久代。本文中,由于笔者主要使用Oracle JDK6.0,因此仍将使用永久代一词。
  方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
  方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法区比堆还多了一个限制:可以选择是否执行垃圾收集。一般的,方法区上执行的垃圾收集是很少的,这也是方法区被称为永久代的原因之一(HotSpot),但这也不代表着在方法区上完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸载。
  在方法区上进行垃圾收集,条件苛刻而且相当困难,效果也不令人满意,所以一般不做太多考虑,可以留作以后进一步深入研究时使用。
  在方法区上定义了OutOfMemoryError:PermGen space异常,在内存不足时抛出。
  运行时常量池(Runtime Constant Pool)是方法区的一部分,用于存储编译期就生成的字面常量、符号引用、翻译出来的直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址,将在类链接阶段完成翻译);运行时常量池除了存储编译期常量外,也可以存储在运行时间产生的常量(比如String类的intern()方法,作用是String维护了一个常量池,如果调用的字符“abc”已经在常量池中,则返回池中的字符串地址,否则,新建一个常量加入池中,并返回地址)。
27.一个对象从创建到销毁都是怎么在这些部分里存活和转移的(Java中对象的创建、内存分配和销毁)
 !!!!!!还没弄明白,先放在这里
28.解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法
堆区:
   1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
   2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身
栈区:
   1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
   2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
   3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
    1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
    2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
29.JVM中哪个参数是用来控制线程的栈堆栈小
-Xss参数用来控制线程的堆栈大小。
30.简述内存分配与回收策略 参考链接
(1)对象优先分配到新生代的Eden区
(2)大对象直接进入老年代:所谓的大对象就是指需要大量连续内存空间的JAVA对象,最典型的大对象就是那种很长的字符串和数组。经常产生大对象容易导致额外的GC操作,JVM中提供了一个-XX:PretenureSizeThreshold参数(这个参数只对Serial和ParNew这两个新生代垃圾收集器有效),令大于这个参数的对象直接在老年代中分配,这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝。为什么这样,就在于Serial使用的是复制算法,如果不了解可以参看之前的介绍JVM垃圾收集机制的博文。
(3)长期存活的对象将进入老年代
        不知道大家在学习GC机制的时候,有没有疑问:对象什么时候才被放到老年代去,具体是怎么做的?
        我们知道,JVM产生一个对象的时候,首先将其放在新生代的Eden区中,并且随着young gc的产生,大部分的对象都被回收了,那么“熬过”这次GC的对象呢?JVM给了每个对象一个“年龄计数器”,所谓的年龄计数器就是指,这个对象熬过第一次GC,并且进入了Survivor区中,那么就将这个对象的年龄设为1,之后,每熬过一次GC,年龄+1,当这个值到达一个阀值(默认15,可通过-XX:MaxTenuringThreshold来设置)时,这个对象就会被移到老年代中。
(4)动态对象年龄判断
为了更好的适应不同程序的内存状况,JVM也不是要去一个对象必须达到MaxTenuringThreshold设置的年龄阀值才能进入老年代。如果Survivor中的对象满足同年龄(比如N)对象所占空间达到了Survivor总空间的一半的时候,那么年龄大于或者等于N的对象都可以进入老年代,无需等待阀值。
(5)空间分配担保
        在学习JVM垃圾收集机制的时候,我们就知道了新生代采用复制算法,但是会造成空间的浪费,故而提出了一种“空间担保机制”来提高复制算法的空间利用率,使复制算法的浪费从50%降到了10%。而老年代的内存就充当了这个担保者,并且由于没有其他内存来担保老年代,所以老年代如果不想产生空间内存碎片那么只能使用“标记-整理”算法了。看到这,我们其实心里肯定有疑问——如何保证老年代有足够的空间来执行空间担保机制呢?Full GC,是否触发根据经验值判断,即使不允许担保失败,也有可能发生担保失败。
        当发生YGC的时候,JVM都会检测之前每次晋升到老年代的对象的平均大小是否大于老年代的剩余内存空间,如果大于,则触发Full GC;如果小于,则查看HandlePromotionFailure设置是否允许担保失败;如果允许,则不会触发Full GC,反之,触发Full GC,保证老年代有足够的空间支持空间分配担保成功。
        其实在每次GC发生的时候,我们也不知道到底会有多少对象被回收,又有多少对象能存活。故而只好取之前每次回收晋升到老年代的对象的平均值作为经验值来判断,但是如果某次GC后存活对象激增,任然会导致担保失败,那么只能重新进行Full GC了,虽然这样会绕个圈子,但是大部分情况下还是会将HandlePromotionFailure的值设为true,从而 避免Full GC过于频繁。换句话说,就是大部分情况,允许担保失败
31.简述重排序,内存屏障,happen-before,主内存,工作内存 参考链接
重排序:大多数现代微处理器都会采用将指令乱序执行(out-of-order execution,简称OoOE或OOE)的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待3。通过乱序执行的技术,处理器可以大大提高执行效率。

内存屏障:内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。
内存屏障可以被分为以下几种类型
LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
有的处理器的重排序规则较严,无需内存屏障也能很好的工作,Java编译器会在这种情况下不放置内存屏障。
为了实现上一章中讨论的JSR-133的规定,Java编译器会这样使用内存屏障。

happen—before
Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,其意思就是说,在发生操作B之前,操作A产生的影响都能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等,它与时间上的先后发生基本没有太大关系。这个原则特别重要,它是判断数据是否存在竞争、线程是否安全的主要依据。

JVM将内存组织为主内存工作内存两个部分。
主内存主要包括本地方法区和堆。
每个线程都有一个工作内存,工作内存中主要包括两个部分,一个是属于该线程私有的栈和对主存部分变量拷贝的寄存器(包括程序计数器PC和cup工作的高速缓存区)。  
1.所有的变量都存储在主内存中(虚拟机内存的一部分),对于所有线程都是共享的。
2.每条线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。
3.线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。
32.Java中存在内存泄漏问题吗?请举例说明 参考链接
在Java语言中,判断一个内存空间是否符合垃圾回的标准有两个:
第一:给对象赋予了空值null,以后再没有被使用过;
第二:给对象赋予了新值,重新分配了内存空间。
一般来讲,内存泄漏主要有两种情况:
一是在堆中申请了空间没有被释放;
二是对象已不再被使用,但还仍然在内存中保留着。
垃圾回收机制的引入可以有效地解决第一种情况;而对于第二种情况,垃圾回收机制则无法保证不再使用的对象会被释放。因此Java语言中的内存泄漏主要指的第二种情况。
Java语言中,容易引起内存泄漏的原因有很多,主要有以下几个方面的内容:
(1)静态集合类,例如HashMap和Vector。如果这些容器为静态的,由于它们的声明周期与程序一致,那么容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。
(2)各种连接,例如数据库的连接、网络连接以及IO连接等。
(3)监听器。在Java语言中,往往会使用到监听器。通常一个应用中会用到多个监听器,但在释放对象的同时往往没有相应的删除监听器,这也可能导致内存泄漏。
(4)变量不合理的作用域。一般而言,如果一个变量定义的作用域大于其使用范围,很有可能会造成内存泄漏,另一方面如果没有及时地把对象设置为Null,很有可能会导致内存泄漏的放生
(5)单例模式可能会造成内存泄漏
33.简述 Java 中软引用(SoftReferenc)、弱引用(WeakReference)和虚引用
强引用(StrongReference)
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它
当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象。  
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就是用弱引用。
虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
34.内存映射缓存区是什么 参考链接
内存映射文件的作用是使一个磁盘文件与存储空间中的一个缓冲区建立映射关系,然后当从缓冲区中取数据,就相当于读文件中的相应字节;而将数据存入缓冲区,就相当于写文件中的相应字节。这样就可以不使用read和write直接执行I/O了。

操作系统的内存管理.一般操作系统的内存分两部分:物理内存;虚拟内存.虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换". MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.MappedByteBuffer 只是一种特殊的 ByteBuffer ,即是ByteBuffer的子类。 MappedByteBuffer 将文件直接映射到内存(这里的内存指的是虚拟内存,并不是物理内存)。通常,可以映射整个文件,如果文件比较大的话可以分段进行映射,只要指定文件的那个部分就可以。

FileChannel提供了map方法来把文件影射为内存映像文件: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了 可访问该内存映像文件的方式:READ_ONLY,READ_WRITE,PRIVATE.
35.jstack,jstat,jmap,jconsole怎么用 JDK内置工具总结
jps主要用来输出JVM中运行的进程状态信息。语法格式如下:
jps [options] [hostid]
-q 不输出类名、Jar名和传入main方法的参数
-m 输出传入main方法的参数
-l 输出main类或Jar的全限名
-v 输出传入JVM的参数

jstack
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:
jstack [option] pid
jstack [option] executable core
jstack [option] [[email protected]]remote-hostname-or-ip
命令行参数选项说明如下:
-l long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
-m mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法)
使用时需要先把进程转换成16进制
printf "%x\n" 21742

jmap(Memory Map)和jhat(Java Heap Analysis Tool)
jmap用来查看堆内存使用状况,一般结合jhat使用。
jmap语法格式如下:
jmap [option] pid
jmap [option] executable core
jmap [option] [[email protected]]remote-hostname-or-ip
如果运行在64位JVM上,可能需要指定-J-d64命令选项参数。
jmap -permstat pid
使用jmap -heap pid查看进程堆内存使用情况,包括使用的GC算法、堆配置参数和各代中堆内存使用情况。
使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象

jstat(JVM统计监测工具)
语法格式如下:
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
vmid是Java虚拟机ID,在Linux/Unix系统上一般就是进程ID。interval是采样时间间隔。count是采样数目

hprof(Heap/CPU Profiling Tool)
hprof能够展现CPU使用率,统计堆内存使用情况。
语法格式如下:
java -agentlib:hprof[=options] ToBeProfiledClass
java -Xrunprof[:options] ToBeProfiledClass
javac -J-agentlib:hprof[=options] ToBeProfiledClass

jconsole:一个java GUI监视工具,可以以图表化的形式显示各种数据。并可通过远程连接监视远程的服务器VM。用java写的GUI程序,
用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。命令行里打 jconsole,选则进程就可以了。需要注意的就是在运行
jconsole之前,必须要先设置环境变量DISPLAY,否则会报错误,Linux下设置环境变量如下:export DISPLAY=:0.0


36.32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?32 位和 64 位的 JVM,int 类型变量的长度是多数? 参考链接
理论上说上 32 位的 JVM 堆内存可以到达 2^32,即 4GB,但实际上会比这个小很多。不同操作系统之间不同,如 Windows 系统大约 1.5 GB,Solaris 大约 3GB。64 位 JVM允许指定最大的堆内存,理论上可以达到 2^64,这是一个非常大的数字,实际上你可以指定堆内存大小到 100GB。甚至有的 JVM,如 Azul,堆内存到 1000G 都是可能的。
32 位和 64 位的 JVM 中,int 类型变量的长度是相同的,都是 32 位或者 4 个字节。
37.怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位
public static void main(String args[]){  
    String arch = System.getProperty("sun.arch.data.model");  
    System.out.println("jdk的版本为:"+arch);  
}
38.JVM自身会维护缓存吗?是不是在堆中进行对象分配,操作系统的堆还是JVM自己管理堆
是的,JVM自身会管理缓存,它在堆中创建对象,然后在栈中引用这些对象。
39.什么情况下会发生栈内存溢出 参考链接
与线程栈相关的内存异常有两个:
a)StackOverflowError(栈溢出,方法调用层次太深,内存不够新建栈帧)
b)OutOfMemoryError(线程太多,内存不够新建线程)
栈溢出抛出java.lang.StackOverflowError错误,出现此种情况是因为方法运行的时候,请求新建栈帧时,栈所剩空间小于战帧所需空间。
例如,通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常
40.双亲委派模型是什么 参考链接
类加载器:
启动类加载器(Bootstrap   ClassLoader):由C++语言实现(针对HotSpot),负责将存放在<JAVA_HOME>\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中。
其他类加载器:由Java语言实现,继承自抽象类ClassLoader。如:
       扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。
       应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。   

相关文章: