java面试之JVM所有知识点合集
1、jvm结构:
1. PC寄存器:
a.
每个线程拥有⼀个
pc
寄存器;
b.
指向下⼀条指令的地址。
2. ⽅法区:
a.
保存装载的类的元信息:类型的常量池,字段、⽅法信息,⽅法字节码
;
jdk6时,String等常量信息置于⽅法区,jdk7移到了堆中;
b.
通常和永久区(
Perm
)关联在⼀起;
3. 堆:
a.
应⽤系统对象都保存在
java
堆中;
b.
所有线程共享
java
堆;
c.
对分代
GC
来说,堆也是分代的;
4. 栈:
a.
线程私有;
b.
栈由⼀系列帧组成(因此
java
栈也叫做帧栈);
c.
帧保存⼀个⽅法的局部变量(局部变量表)、操作数栈、常量池指针;
d.
每⼀次⽅法调⽤创建⼀个帧,并压栈。
2、jvm内存模型:
1.
每⼀个线程有⼀个⼯作内存,和主存独⽴;
2.
⼯作内存存放主存中变量的值的拷⻉;
3.
对于普通变量,⼀个线程中更新的值,不能⻢上反应在其他变量中;如果需要在其他线程中⽴即可⻅,需要使⽤
volatile
关键字;
4.
volatile
不能代替锁,⼀般认为
volatile
⽐锁性能好(不绝对),使⽤
volatile
的条件是语义是否满⾜应⽤;
5.
可⻅性:⼀个线程修改了变量,其他线程可以⽴即知道。
a.
volatile
;
b.
synchronized
(
unlock
之前,写变量值回主存);
c.
final
(⼀旦初始化完成,其他线程可⻅)。
3、java四引⽤:
1. 强引⽤:
强引⽤是使⽤最普遍的引⽤。如果⼀个对象具有强引⽤,那垃圾回收器绝不会回收它。当内存空间不⾜,
Java
虚拟机宁愿抛出
OutOfMemoryError
错误,使程序异常终⽌,也不会靠随意回收具有强引⽤的对象来解决内存不⾜的问题。
2. 软引⽤:
如果内存空间不⾜了,就会回收这些对象的内存。只要垃圾回收器没有回收它,软引⽤可以和⼀个引⽤队列
(
ReferenceQueue
)联合使⽤,如果软引⽤所引⽤的对象被垃圾回收器回收,
Java
虚拟机就会把这个软引⽤加⼊到与之关联的引⽤队列
中。
3. 弱引⽤:
弱引⽤与软引⽤的区别在于:只具有弱引⽤的对象拥有更短暂的⽣命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程
中,⼀旦发现了只具有弱引⽤的对象,不管当前内存空间⾜够与否,都会回收它的内存。
弱引⽤可以和⼀个引⽤队列(
ReferenceQueue
)联合使⽤,如果弱引⽤所引⽤的对象被垃圾回收,
Java
虚拟机就会把这个弱引
⽤加⼊到与之关联的引⽤队列中。
4. 虚引⽤:
虚引⽤在任何时候都可能被垃圾回收器回收,主要⽤来跟踪对象被垃圾回收器回收的活动,被回收时会收到⼀个系统通知。虚
引⽤与软引⽤和弱引⽤的⼀个区别在于:虚引⽤必须和引⽤队列 (
ReferenceQueue
)联合使⽤。当垃圾回收器准备回收⼀个对象时,
如果发现它还有虚引⽤,就会在回收对象的内存之前,把这个虚引⽤加⼊到与之关联的引⽤队列中。
4、GC算法分类:
1. 引⽤计数法(没有被java采⽤):
a.
原理:对于⼀个对象
A
,只要有任何⼀个对象引⽤了
A
,则
A
的引⽤计数器就加
1
,当引⽤失效时,引⽤计数器就减
1
,只要对
象
A
的引⽤计数器的值为
0
,则对象
A
就会被回收。
b.
问题:
i.
引⽤和去引⽤伴随加法和减法,影响性能;
ii.
很难处理循环引⽤。
2. 标记清除法:
a.
原理:
现代垃圾回收算法的思想基础。标记
-
清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。⼀种可⾏的实现是,
在标记节点,⾸先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引⽤的垃圾对象。然后在
清除阶段,清除所有未被标记的对象。
b.
问题:
i.
标记和清除两个过程效率不⾼,产⽣内存碎⽚导致需要分配较⼤对象时⽆法找到⾜够的连续内存⽽需要触发⼀次
GC
操
作
。
3. 标记压缩法:
a. 原理:
适合⽤于存活对象较多的场合,如⽼年代。它在标记
-
清除算法的基础上做了⼀些优化。标记阶段⼀样,但之后,将所
有存活对象压缩到内存的⼀端。之后,清除边界外所有的空间。
b. 优点:
i.
解决了标记
-
清除算法导致的内存碎⽚问题和在存活率较⾼时复制算法效率低的问题
。
4. 复制算法:
a.
原理:将原有的内存空间分为两块,每次只使⽤其中⼀块,在垃圾回收时,将正在使⽤的内存中的存活对象复制到未使⽤的
内存块中,之后清除正在使⽤的内存块中的所有对象,交换两个内存的⻆⾊,完成垃圾回收。
b.
问题:
i.
不适⽤于存活对象⽐较多的场合,如⽼年代
。
5.
分代回收法:
a.
原理:根据对象存活周期的不同将内存划分为⼏块,⼀般是新⽣代和⽼年代,新⽣代基本采⽤复制算法,⽼年代采⽤标记整
理算法。
5、MinorGC & FullGC:
1.
Minor GC
通常发⽣在新⽣代的
Eden
区
,在这个区的对象⽣存期短,往往发⽣
GC
的频率较⾼,回收速度⽐较快,⼀般采⽤复制
-
回 收算法。
2.
Full GC/Major GC
发⽣在⽼年代,⼀般情况下,触发⽼年代
GC
的时候不会触发
Minor GC
,所采⽤的是标记
-
清除算法。
6、垃圾收集器:
1.
Serial New
收集器是针对新⽣代的收集器,采⽤的是复制算法;
2.
Parallel New
(并⾏)收集器,新⽣代采⽤复制算法,⽼年代采⽤标记整理;
3.
Parallel Scavenge
(并⾏)收集器,针对新⽣代,采⽤复制收集算法;
4.
Serial Old
(串⾏)收集器,新⽣代采⽤复制,⽼年代采⽤标记清理;
5.
Parallel Old
(并⾏)收集器,针对⽼年代,标记整理;
6.
CMS
收集器,基于标记清理;
7.
G1
收集器(
JDK
):整体上是基于标记清理,局部采⽤复制;
综上:新⽣代基本采⽤复制算法,⽼年代采⽤标记整理算法。
cms
采⽤标记清理;
7、java类加载机制:
1.
概念:虚拟机把描述类的数据⽂件(字节码)加载到内存,并对数据进⾏验证、准备、解析以及类初始化,最终形成可以被虚拟
机直接使⽤的
java
类型(
java.lang.Class
对象)。
2.
类⽣命周期:
类加载过程:
读取⼆进制字节流到
jvm—>
验证格式语义等
—>
为静态变量分配内存空间
—>
常量池引⽤解析
—>
执⾏
static
标识的
代码
a.
加载过程:通过⼀个类的全限定名来获取定义此类的⼆进制字节流,将这个字节流所代表的静态存储结构转化为⽅法区的运
⾏时数据结构。在内存中(⽅法区)⽣成⼀个代表这个类的
java.lang.Class
对象,作为⽅法区这个类的各种数据的访问⼊⼝;
b.
验证过程:为了确保
Class
⽂件的字节流中包含的信息符合当前虚拟机的要求,⽂件格式验证、元数据验证、字节码验证、符
号引⽤验证;
c.
准备过程:正式为类属性分配内存并设置类属性初始值的阶段,这些内存都将在⽅法区中进⾏分配;
准备阶段,static对象会被设置默认值,static fifinal对象会被赋上给予的值。
d.
解析阶段:虚拟机将常量池内的符号引⽤替换为直接引⽤的过程。
i.
符号引⽤:字符串,引⽤对象不⼀定被加载;
ii.
直接引⽤:指针或者地址偏移量,引⽤对象⼀定在内存中。
e.
初始化阶段:类初始化阶段是类加载过程的最后⼀步。初始化阶段就是执⾏类构造器
<clint>
()⽅法的过程。
f.
使⽤阶段:
g.
卸载阶段:
8、类加载器:
1. java默认提供三个类加载器:
a.
BootStrap ClassLoader
启动
ClassLoader
(
sun.boot.class.path
):
最顶层的加载类,主要加载
jdk
中的核⼼库,
%JRE_HOME%\lib
下的
rt.jar
、
resources.jar
、
charsets.jar
和
class
等。
Bootstrap ClassLoader不继承⾃ClassLoader,因为它不是⼀个普通的Java类,底层由C++编写,已嵌⼊到了JVM内核当中,当JVM启动
后,Bootstrap ClassLoader也随着启动,负责加载完核⼼类库后,并构造Extension ClassLoader和App ClassLoader类加载器。
b.
Extension ClassLoader
扩展
ClassLoader
(
java.ext.dirs
):扩
展的类加载器,加载⽬录
%JRE_HOME%\lib\ext
⽬录下的
jar
包
和
class
⽂件。还可以加载
-D java.ext.dirs
选项指定的⽬录。
c.
App ClassLoader
应⽤
ClassLoader/
系统
ClassLoader
(
java.class.path
):
也称为
SystemAppClass
加载当前应⽤的
classpath
的所有类。
除了BootStrap ClassLoader,每个ClassLoader都有⼀个Parent作为⽗亲。
2. 双亲委派机制:
a.
定义:当⼀个
ClassLoader
实例需要加载某个类时,它会试图亲⾃搜索某个类之前,先把这个任务委托给它的⽗类加载器,这
个过程是由上⾄下依次检查的,⾸先由最顶层的类加载器
Bootstrap ClassLoader
试图加载,如果没加载到,则把任务转交给
Extension ClassLoader
试图加载,如果也没加载到,则转交给
App ClassLoader
进⾏加载,如果它也没有加载得到的话,则返
回给委托的发起者,由它到指定的⽂件系统或⽹络等
URL
中加载该类。如果它们都没有加载到这个类时,则抛出
ClassNotFoundException
异常。否则将这个找到的类⽣成⼀个类的定义,并将它加载到内存当中,最后返回这个类在内存中的
Class
实例对象。
b. 作⽤:
i.
避免重复加载;
ii.
考虑到安全因素,避免⾃定义的类去替代系统类,如
String
。
c.
jvm
如何判定两个
class
是否相同?
JVM
在判定两个
class
是否相同时,不仅要判断两个类名是否相同,⽽且要判断是否由同⼀个类加
载器实例加载的。只有两者同时满⾜的情况下,
JVM
才认为这两个
class
是相同的。
1. ⾃底向上检查类是否已经加载;
2. ⾃顶向下尝试加载类。
3.
custom classloader
:⾃定义
classloader
a.
Java
中提供的默认
ClassLoader
,只加载指定⽬录下的
jar
和
class
,如果我们想加载其它位置的类或
jar
时,就需要定义⾃⼰的
ClassLoader
。
b.
步骤:
i.
继承
java.lang.ClassLoader
ii.
重写⽗类的
findClass
⽅法
9、引起类加载的五个⾏为:
1.
遇到
new
、
getstatic
、
putstatic
或
invokestatic
这四条字节码指令
2.
反射调⽤的时候,如果类没有进⾏过初始化,则需要先触发其初始化
3.
⼦类初始化的时候,如果其⽗类还没初始化,则需先触发其⽗类的初始化
4.
虚拟机执⾏主类的时候(有
main
(
string
[]
args
))
5.
JDK1.7
动态语⾔⽀持
10、java对象创建时机:
1.
使⽤
new
关键字创建对象
2.
使⽤
Class
类的
newInstance
⽅法(反射机制)
3.
使⽤
Constructor
类的
newInstance
⽅法(反射机制)
4.
使⽤
Clone
⽅法创建对象
5.
使⽤(反)序列化机制创建对象
11、jvm调优:
1. 调优时机:
a.
heap
内存(⽼年代)持续上涨达到设置的最⼤内存值;
b.
Full GC
次数频繁;
c.
GC
停顿时间过⻓(超过
1
秒);
d.
应⽤出现
OutOfMemory
等内存异常;
e.
应⽤中有使⽤本地缓存且占⽤⼤量内存空间;
f.
系统吞吐量与响应性能不⾼或下降。
2. 调优原则:
a.
多数的
Java
应⽤不需要在服务器上进⾏
JVM
优化;
b.
多数导致
GC
问题的
Java
应⽤,都不是因为我们参数设置错误,⽽是代码问题;
c.
在应⽤上线之前,先考虑将机器的
JVM
参数设置到最优(最适合);
d.
减少创建对象的数量;
e.
减少使⽤全局变量和⼤对象;
f.
JVM
优化是到最后不得已才采⽤的⼿段;
g.
在实际使⽤中,分析
GC
情况优化代码⽐优化
JVM
参数更好;
3. 调优⽬标:
a.
GC
低停顿;
b.
GC
低频率;
c.
低内存占⽤;
d.
⾼吞吐量;
4. 调优步骤:
a.
分析
GC
⽇志及
dump
⽂件,判断是否需要优化,确定瓶颈问题点;
b.
确定
jvm
调优量化⽬标;
c.
确定
jvm
调优参数(根据历史
jvm
参数来调整);
d.
调优⼀台服务器,对⽐观察调优前后的差异;
e.
不断的分析和调整,知道找到合适的
jvm
参数配置;
f.
找到最合适的参数,将这些参数应⽤到所有服务器,并进⾏后续跟踪。
12、jvm调优参数:
1.
设定堆内存⼤⼩,这是最基本的。
2.
-Xms
:启动
JVM
时的堆内存空间。
3.
-Xmx
:堆内存最⼤限制。
4.
设定新⽣代⼤⼩。
5.
新⽣代不宜太⼩,否则会有⼤量对象涌⼊⽼年代。
6.
-XX:NewRatio
:新⽣代和⽼年代的占⽐。
7.
-XX:NewSize
:新⽣代空间。
8.
-XX:SurvivorRatio
:伊甸园空间和幸存者空间的占⽐。
9.
-XX:MaxTenuringThreshold
:对象进⼊⽼年代的年龄阈值。
10. 设定垃圾回收器
年轻代:-XX:+UseParNewGC。
⽼年代:-XX:+UseConcMarkSweepGC
。
CMS
可以将
STW
时间降到最低,但是不对内存进⾏压缩,有可能出现“并⾏模式失败”。⽐如⽼年代空间还有
300MB
空间,但是
⼀些
10MB
的对象⽆法被顺序的存储。这时候会触发压缩处理,但是
CMS GC
模式下的压缩处理时间要⽐
Parallel GC
⻓很多。
G1
采⽤”标记
-
整理“算法,解决了内存碎⽚问题,建⽴了可预测的停顿时间类型,能让使⽤者指定在⼀个⻓度为
M
毫秒的时间段
内,消耗在垃圾收集上的时间不得超过
N
毫秒。
13、触发full gc的场景及应对策略:
1.
System.gc
()⽅法的调⽤,应对策略:通过
-XX:+DisableExplicitGC
来禁⽌调⽤
System.gc
;
2.
⽼年代代空间不⾜,应对策略:让对象在
Minor GC
阶段被回收,让对象在新⽣代多存活⼀段时间,不要创建过⼤的对象及数组;
3.
永⽣区空间不⾜,应对策略:增⼤
PermGen
空间;
4.
GC
时出现
promotionfailed
和
concurrent mode failure
,应对策略:增⼤
survivor space
;
5.
Minor GC
后晋升到旧⽣代的对象⼤⼩⼤于⽼年代的剩余空间,应对策略:增⼤
Tenured space
或下调
CMSInitiatingOccupancyFraction=60
;
6.
内存持续增涨达到上限导致
Full GC
,应对策略:通过
dumpheap
分析是否存在内存泄漏。
14、jvm堆的基本结构:
1、JVM中堆空间可以分成三个⼤区,新⽣代、⽼年代、永久代
2
、新⽣代可以划分为三个区,
Eden
区,两个
Survivor
区,在
HotSpot
虚拟机
Eden
和
Survivor
的⼤⼩⽐例为
8f1
15、如何查看jvm内存使⽤情况:
可以使⽤
JDK
⾃带的
JConsole
、
JVisualVM
、
JMap
、
JHat
等⼯具,或者使⽤第三⽅⼯具,⽐如
Eclipse Memory Analyzer
16、jvm内存溢出例⼦:
1.
内存溢出,⽐如给
JVM
分配的内存不够⼤,或者程序中存在死循环⼀直申请内存。
2.
内存泄露,⽐如下⾯这段代码,
list
持有
o
的引⽤,
o
暂时是⽆法被
JVM
垃圾回收的,只有当
list
被垃圾回收或者
o
从对象
list
删除掉
后,
o
才能被
JVM
垃圾回收。
1
List
<
Object
>
list
=
new
ArrayList
<>
();
2
Object o
=
new
Object
();
3
list
.
add
(
o
);
4
o
=
null
;
17、常⽤的GC策略,什么时候会触发YGC,什么时候触发FGC:
1.
YGC
(
Young GC
):
a.
概念:对新⽣代堆进⾏
GC
。频率⽐较⾼,因为⼤部分对象的存活寿命较短,在新⽣代⾥被回收。性能耗费较⼩。
b.
触发时机:
Eden
区空间不⾜。
2.
FGC
(
Full GC
):
a.
概念:全堆范围的
GC
。默认堆空间使⽤到达
80%
(可调整)的时候会触发
FGC
。以我们⽣产环境为例,⼀般⽐较少会触发
FGC
,有时
10
天或⼀周左右会有⼀次。
b.
触发时机:
1
、
Old
空间不⾜;
2
、
Perm
空间不⾜;
3
、显示调⽤
System.gc
() ,包括
RMI
等的定时触发;
4
、
YGC
时的悲观策略;
5
、
dump live
的内存信息时(
jmap –dump:live
)。
18、获得Class对象的⽅式
1.
静态类的
.class
语法:
GuideUtil.class
2.
普通类对象的
getClass
()⽅法:
new Test
()
.getClass
()
3.
通过
Class
对象的
forName
()⽅法:
Class.forName
(“
com.zhenai.modules.guide.utils.GuideUtil"
);
4.
对于包装类,可以通过
.TYPE
语法⽅式:
Integer.TYPE
19、内存溢出的可能原因和解决⽅法
A
,数据加载过多,如
1
次从数据库中取出过多数据
B
,集合类中有对对象的引⽤,⽤完后没有清空或者集合对象未置空导致引⽤存在等,是的
JVM
⽆法回收
C
,死循环,过多重复对象
D
,第三⽅软件的
bug
E
,启动参数内存值设定的过⼩。
解决⽅法:修改
JVM
启动参数,加内存(
-Xms
,
-Xmx
);错误⽇志,是否还有其他错误;代码⾛查
20、内存泄漏的原因?
1.
未对作废数据内存单元置为
null
,尽早释放⽆⽤对象的引⽤,使⽤临时变量时,让引⽤变量在推出活动域后⾃动设置为
null
,暗示
垃圾收集器收集;
2.
程序避免⽤
String
拼接,⽤
StringBuffer
,因为每个
String
会占⽤内存⼀块区域;
3.
尽量少⽤静态变量(全局不会回收);
4.
不要集中创建对象尤其⼤对象,可以使⽤流操作;
5.
尽量使⽤对象池,不再循环中创建对象,优化配置;
6.
创建对象到单例
getInstance
中,对象⽆法回收被单例引⽤;
7.
服务器
session
时间设置过⻓也会引起内存泄漏。
21、⽅法区oom
1.
⽅法区⽤于存放
Class
的相关信息,如:类名,访问修饰符,常量池,字符描述,⽅法描述等。
2.
原因:运⾏时产⽣⼤量的类去填满⽅法区,直到溢出。
22、哪些情况下对象会进⼊⽼年代?
1.
新⽣代对象每次经历⼀次
minor gc
,年龄会加
1
,当达到年龄阈值(默认为
15
岁)会直接进⼊⽼年代;
2.
⼤对象直接进⼊⽼年代;
3.
新⽣代复制算法需要⼀个
survivor
区进⾏轮换备份,如果出现⼤量对象在
minor gc
后仍然存活的情况时,就需要⽼年代进⾏分配
担保,让
survivor
⽆法容纳的对象直接进⼊⽼年代;
4.
如果在
Survivor
空间中相同年龄所有对象⼤⼩的总和⼤于
Survivor
空间的⼀半,年龄⼤于或等于该年龄的对象就可以直接进⼊年⽼
代。
23、jvm中哪些地⽅会出现oom?分别说说oom的可能原因?
1.
java
堆溢出(
heap
):
a.
Java
堆内存主要⽤来存放运⾏过程中所以的对象,该区域
OOM
异常⼀般会有如下错误信息:
1
java
.
lang
.
OutofMemoryError
:
Java heap space
b.
此类错误⼀般通过
Eclipse Memory Analyzer
分析
OOM
时
dump
的内存快照就能分析出来,到底是由于程序原因导致的内存泄
露,还是由于没有估计好
JVM
内存的⼤⼩⽽导致的内存溢出。
c.
另外,
Java
堆常⽤的
JVM
参数:
1
-
Xms
:初始堆⼤⼩,默认值为物理内存的
1
/
64
(
<
1
GB
)
,默认
(
MinHeapFreeRatio
参数可以调整
)
空余堆内存⼩于
40
%
时,
JVM
就会增⼤堆直到
2
-
Xmx
:最⼤堆⼤⼩,默认值为物理内存的
1
/
4
(
<
1
GB
)
,默认
(
MaxHeapFreeRatio
参数可以调整
)
空余堆内存⼤于
70
%
时,
JVM
会减少堆直到
3
-
Xmn
:年轻代⼤⼩
(
1.4
or lator
)
,此处的⼤⼩是(
eden
+
2
survivor space
)
,与
jmap
-
heap
中显示的
New gen
是不同的。
2.
栈溢出(
stack
):
a.
栈⽤来存储线程的局部变量表、操作数栈、动态链接、⽅法出⼝等信息。如果请求栈的深度不⾜时抛出的错误会包含类似下⾯
的信息:
1
java
.
lang
.
StackOverflowError
。
b.
另外,由于每个线程占的内存⼤概为
1M
,因此线程的创建也需要内存空间。
操作系统
可⽤内存
-Xmx-MaxPermSize
即是栈可
⽤的内存,如果申请创建的线程⽐较多超过剩余内存的时候,也会抛出如下类似错误:
java.lang.OutofMemoryError: unable
to create new native thread
c.
相关的
JVM
参数有:
1
-
Xss
:
每个线程的堆栈⼤⼩
,
JDK5.0
以后每个线程堆栈⼤⼩为
1
M
,
以前每个线程堆栈⼤⼩为
256
K
.
2
在相同物理内存下
,
减⼩这个值能⽣成更多的线程
.
但是操作系统对⼀个进程内的线程数还是有限制的
,
不能⽆限⽣成
,
经验值在
3000
~
5000
d.
可能原因:
i.
递归:递归⾥⽤到的局部变量存储在堆栈中,堆栈的访问效率⾼,速度快,但空间有限,递归太多变量需要⼀直⼊栈⽽
不出栈,导致需要的内存空间⼤于堆栈的空间,栈空间是
2M
,堆空间内存空间。
3.
运⾏时常量溢出(
constant
):
a.
运⾏时常量保存在⽅法区,存放的主要是编译器⽣成的各种字⾯量和符号引⽤,但是运⾏期间也可能将新的常量放⼊池中,
⽐如
String
类的
intern
⽅法。如果该区域
OOM
,错误结果会包含类似下⾯的信息:
1
java
.
lang
.
OutofMemoryError
:
PermGen space
b.
相关的
JVM
参数有:
1
-
XX
:
PermSize
:设置持久代
(
perm gen
)
初始值,默认值为物理内存的
1
/
64
2
-
XX
:
MaxPermSize
:设置持久代最⼤值,默认为物理内存的
1
/
4
4.
⽅法区溢出:
a.
⽅法区主要存储被虚拟机加载的类信息,如类名、访问修饰符、常量池、字段描述、⽅法描述等。理论上在
JVM
启动后该区域
⼤⼩应该⽐较稳定,但是⽬前很多框架,⽐如
Spring
和
Hibernate
等在运⾏过程中都会动态⽣成类,因此也存在
OOM
的⻛险。如
果该区域
OOM
,错误结果会包含类似下⾯的信息:
1
java
.
lang
.
OutofMemoryError
:
PermGen space
b.
相关的
JVM
参数有:
1
-
XX
:
PermSize
:设置持久代
(
perm gen
)
初始值,默认值为物理内存的
1
/
64
2
-
XX
:
MaxPermSize
:设置持久代最⼤值,默认为物理内存的
1
/
4
23、如何定位jvm内存信息?
1.
打印⽇志:
1
-
XX
:
+
PrintGC
:输出形式
:
2
[
GC 118250
K
->
113543
K
(
130112
K
),
0.0094143
secs
]
3
[
Full
GC 121376
K
->
10414
K
(
130112
K
),
0.0650971
secs
]
4
-
XX
:
+
PrintGCDetails
:输出形式
:
5
[
GC
[
DefNew
:
8614
K
->
781
K
(
9088
K
),
0.0123035
secs
]
118250
K
->
113543
K
(
130112
K
),
0.0124633
secs
]
6
[
GC
[
DefNew
:
8614
K
->
8614
K
(
9088
K
),
0.0000665
secs
][
Tenured
:
112761
K
->
10414
K
(
121024
K
),
0.0433488
secs
7
-
XX
:
+
PrintGCTimeStamps
:打印
GC
停顿耗时
8
-
XX
:
+
PrintGCApplicationStoppedTime
:打印垃圾回收期间程序暂停的时间
.
9
-
XX
:
+
PrintHeapAtGC
:打印
GC
前后的详细堆栈信息
10
-
Xloggc
:
filename
:把相关⽇志信息记录到⽂件以便分析
.
2.
错误调试:
1
-
XX
:
ErrorFile
=
.
/
hs_err_pid
<
pid
>
.
log
:如果
JVM
crashed
,将错误⽇志输出到指定⽂件路径。
2
-
XX
:
HeapDumpPath
=
.
/
java_pid
<
pid
>
.
hprof
:堆内存快照的存储⽂件路径。
3
-
XX
:
-
HeapDumpOnOutOfMemoryError
:在
OOM
时,输出⼀个
dump
.
core
⽂件,记录当时的堆内存快照
3.
类状态器相关:
1
-
XX
:
-
TraceClassLoading
:打印
class
装载信息到
stdout
。记
Loaded
状态。
2
-
XX
:
-
TraceClassUnloading
:打印
class
的卸载信息到
stdout
。记
Unloaded
状态。
24、当对象A创建之后,对象A在各个区之间的流转过程
1.
jvm
堆结构图:
a.
新⽣代通常占
JVM
堆内存的
1/3
,因为新⽣代存储都是新创建的对象,⽐较⼩的对象,⽽⽼年代存的都是⽐较⼤的,活的久的
对象,所以⽼年代占
JVM
堆内存较⼤;
b.
新⽣代⾥的
Eden
区通常占年轻代的
4/5
,两个
Survivor
分别占新⽣代的
1/10
。因为
Survivor
中存储的是
GC
之后幸存的对象,实
际上只有很少⼀部分会幸存,所以
Survivor
占的⽐例⽐较⼩。
2.
对象流转流程(新⽣代复制算法,可以减少内存碎⽚):
a.
对象
A
被
new
出来之后,是被存放在
Eden
(伊甸园)区的。
b.
当发⽣⼀次
GC
后,
Eden
区存活下来的对象
A
会被复制到
s1
区,
s0
中存活的对象也会被复制到
s1
中。
如果对象年龄超过阈值年龄(默认15岁),会被复制到⽼年区。
部分对象也需要⽼年代分担。
c.
GC
会清空
Eden
和
s0
中存储的所有对象;
d.
交换
s0
和
s1
的⻆⾊;
e.
重复上⾯的步骤。
25、jvm堆持久代:
⽤于存放静态类型数据,如
Java Class
,
Method
等。持久代对垃圾回收没有显著影响。但是有些应⽤可能动态⽣成或调⽤⼀些
Class
,例如
Hibernate CGLib
等,在这种时候往往需要设置⼀个⽐较⼤的持久代空间来存放这些运⾏过程中动态增加的类型。
26、synchronized和ReentrantLock的实现机制?
synchronized
⽤的锁是存在
Java
对象头⾥的