java面试 jvm总结

jvm调优总结(1):一些概念

数据类型分类基本类型和引用类型
基本类型:double float int long short byte boolean char
引用类型包括:类类型,接口类型,数组

堆与栈
栈是运行时的单位,而堆是存储的单位

为什么要把堆和栈区分出来呢?栈中不是也可以存储数据吗?
1、从软件设计的角度看,栈代表了处理逻辑,而堆代表了数据
2、堆和栈的分离,使堆中的内容可以被多个栈共享(理解为多个线程访问同一对象)
3、栈的存储能力有限,堆中的对象是根据需要动态增长的。拆分使动态增长成为可能,栈只需记录堆中唯一的地址即可
4、面向对象时堆和栈的完美结合 ,对象的属性是数据,对象的方法,就是运行逻辑

堆中存什么?栈中存什么?
堆中存的是对象,栈中存的是基本数据类型和堆中对象的引用。
一个对象的大小是动态变化的,但在栈中,一个对象只对应了一个4byte的引用

为什么不把基本类型放堆中?因为其占用的空间比较少且长度固定,因此栈中存储就够了,如果存在堆中是没什么意义的。

但是基本类型、对象引用和对象本身就有所区别了,因为一个是栈中的数据,一个是堆中的数据。
常见的问题:java参数传递如何传值?
1、不与C做类比,java中没有指针的概念
2、程序永远都是在栈中进行,因而参数传递时,只存在传递基本类型和对象引用的问题,不会直接传对象本身
所以它都是进行传值调用

基本类型和引用的处理是一样的,都是传值

可以把一个对象看成一棵树,对象的属性如果还是对象,则还是一棵树,基本类型则为树的叶子节点。

堆是一块共享的内存


java对象的大小

基本数据的类型的大小是固定的,
非基本数据类型大小不固定

在java中,一个空object对象的大小是8byte。新建一个对象,所占的空间为8+4byte。(4byte是对象的引用)

注意基本类型的包装类型的大小,包装类型大小至少12byte,因为java对象大小是8的倍数,因此,基本数据类型包装类大小至少是16byte。
因此,可能的话尽量少用包装类,jdk5.0后有自动类型转换,因此,jvm会在存储方面进行优化


引用类型

对象的引用类型分为强引用、软引用、弱引用、虚引用

强引用不会被垃圾回收

弱引用是下次垃圾回收一定会被回收

jvm调优总结(2):基本垃圾回收算法

按基本回收策略分

引用计数:
比较古老的回收算法,原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。
垃圾回收时,只用收集计算为0的对象,但无法处理循环引用问题

标记-清除:
此算法执行分两个阶段。第一阶段从引用根节点开始标记所有被引用的对象。
第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时会产生内存碎片	

复制
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。
垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。
此算法每次只处理正在使用的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现碎片问题。
缺点:需要两倍的内存空间

标记-整理:
此算法结合了"标记-清除"和"复制"两个算法的优点。
第一阶段从根节点开始标记所有被引用对象
第二阶段遍历整个堆,清除未标记对象并且把存活对象"压缩"到堆的其中一块,按顺序排放。
避免了碎片问题,也避免了复制的空间问题


按分区对象的方式分

增量收集:
实时垃圾回收算法,即在应用进行的同时进行垃圾回收。

分代收集:
基于对对象生命周期分析后得出的垃圾回收算法。
把对象分为年轻代、年老代、持久代。对不同生命周期的对象使用不同的算法进行回收。现在普遍用此算法


按系统线程分

串行收集:
使用单线程处理所有垃圾回收工作,适合单处理器机器

并行收集:
并行收集使用多线程处理垃圾回收工作,适合CPU数目多的机器

并发收集:
相对于串行收集和并行收集,前面两个进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行。
因此,系统在垃圾回收时会有明显的暂停,暂停时间会因为堆越大而长

jvm调优总结(3):垃圾回收面临的问题

如何区分垃圾?

垃圾回收从哪儿开始的呢?从哪儿开始查找哪些对象时正在被当前系统使用的。
上面分析过堆和栈的区别,其中栈是真正进行程序执行地方,所以要获得哪些对象正在被使用,需要从java栈开始。
同时,一个栈与一个线程对应,因此多线程必须对这些线程对应的栈进行检查
除了栈外,还有系统运行时的寄存器等,也是存储运行数据的。
这样以栈或寄存器中的引用为起点,我们开找到堆中的对象,有从对象找到对堆中其他对象的引用,这种引用逐步扩展,最终以null引用或基本类型结束,这样就形成多颗对象树。
在这些对象树上的对象,都是当前系统运行所需要的对象,不能被垃圾回收。
而其他剩余对象,则视为无法被引用的对象,可以被当做垃圾进行回收。

因此,垃圾回收的起点是一些根对象(java栈,静态变量,寄存器),最简单的java栈就是java程序执行的main函数。
这种回收方式,也是上面提到的"标记-清除"


如何处理碎片

由于不同java对象存活时间时不一定的,因此,在程序运行一段时间以后,如果不进行内存整理,就会出现零散的内存碎片。
碎片最直接的问题会导致无法分配大块的内存空间,以及程序运行效率低。
所以,上面提到的基本算法都能解决碎片问题


如何解决同时存在的对象创建和对象回收问题

垃圾回收线程是回收内存的,而程序运行线程则是分配内存,一个回收内存,一个分配内存。看起来很矛盾。
在现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用。

但这种方式有弊端,当堆空间持续增大时,垃圾回收的时间也将相应的持续增大,对应暂停的时间也将增加。
为了解决这个矛盾,有了并发垃圾回收算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题。
但是因为需要在新生成对象的同时又想回收对象,算法复杂性会大大增加,系统的处理能力也会降低,碎片问题会比较难解决。

jvm调用总结(4):分代垃圾回收

为什么要分代?

分代的垃圾回收策略,基于:不同对象的生命周期是不一样的。
因此不同生命周期的对象可以采取不同的手机方式,以便提高回收效率。

在java程序运行过程中,会产生大量的对象,其中有些对象是与业务信息相关,比如http请求中session对象,线程,socket,这类对象与业务相关,因此生命周期长。
有一些对象生命周期短,比如String对象,其不变的特性,系统会产生大量的对象,有些对象甚至只用一次即可

如果不分代,每次回收都遍历所有存活对象,对于生命周期长的对象,这种遍历没效果,可能遍历过多次且时间长。
分代垃圾回收采用分治的思想,进行代的划分,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收


如何分代

年轻代、年老代、持久代。
持久代主要存放的是java类的类信息,与垃圾收集要收集的java对象关系不大。
年轻代和年老代的划分是对垃圾收集影响比较大的

年轻代
所有新生成的对象首先是放在年轻代。年轻代的目标是收集掉那些生命周期短的对象。
年轻代分为三个区(一个Eden和两个Survivor区)。
大部分对象在Eden区生成,当eden区满时,还存活的对象将被复制到survivor区,当这个survivor区满时,此区的存活对象将被复制到另外一个survivor区,当这个区也满了,从第一个survivor复制的并且还存活的对象将被复制到年老区。

年老代
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代。存放的对象生命周期比较长

持久代:
用于存放静态文件,如java类、方法等。


什么情况下触发垃圾回收

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型Scavenge GC和Full GC

Scavenge GC
当新对象生成,并且在Eden申请空间失败时,就会触发GC,对Eden进行GC,清除非存活对象,并把存活对象移动到survivor区。然后整理survivor的两个区。
这种GC是对年轻代的eden进行。因为大多对象从eden区开始,不会分配太大,所以经常GC

Full GC
对整个堆进行整理。速率慢,应减少Full GC的次数。
如下原因可能导致Full GC
1、年老代被写满
2、持久代被写满
3、system.gc()被显示调用


选择合适的垃圾收集算法

串行处理器
适用于数据量小
缺点:只适用于小型应用

并行处理器
使用于"对吞吐量有高要求",多CPU、对应用响应时间无要求的中、大型应用
缺点:垃圾收集过程中应用响应时间可能加长

并行处理器
适用于"对响应时间有高要求",多CPU、对应用响应时间有较高要求的中、大型应用
例如:web服务器,

jvm调优总结(5):典型配置

典型设置
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

堆设置
-Xmx3550m:设置jvm最大可用内存为3550m
-Xms3550m:设置jvm初始堆内存为3550m
-Xmn2g:设置年轻代大小为2G
-Xss128k:设置每个线程堆栈大小
-XX:NewRetio=4:设置年轻代与年老代的比例
-XX:SurvivorRatio=4:设置年轻代中eden与survivor区的大小比值
-XX:MaxPermSize=16m:设置持久代大小为16m
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄,设置为0,则年轻代对象不经过survivor,直接进年老代
	
收集器设置
-XX:+UseSerialGC 设置串行收集器
-XX:+UsePaeallelGC 设置并行收集器
-XX:+UseParealledlOldGC 设置并行年老代收集器
-XX:+UseConcMarkSweepGC 设置并行收集器

垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

并行收集器设置
-XX:ParallGCThreads=n 设置并行收集器收集时使用的CPU数。并行收集线程数
-XX:MaxGCPauseMills=n 设置并行收集最大暂停时间
-XX:GCTimeRatio=n 	  设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

并发收集器设置
-XX:+CMSIncrementalMode	设置为增量模式。适用于单CPU情况
-XX:ParallGCThreads=n	并行收集器线程数


调优总结

年轻代大小选择

响应时间优先的应用:尽可能设大,知道接近系统的最低响应时间限制
吞吐量优先的应用:尽可能设大,可能到达Gbit的程度

年老代大小选择

响应时间优先的应用:
年老代使用并发收集器,一般考虑并发会话率和会话持续率等一些参数
堆设小了可能会造成内存碎片、高回收频率
堆社大了,需要较长的收集时间

吞吐量优先的应用
一般吞吐量优先应用都有个很大的年轻代和一个小的年老代
这样年老代存放长期存活的对象

较小堆引用的碎片问题
因为年老代的并发收集器使用标记-清除算法,所以不会对堆进行压缩
如果出现碎片需要如下配置
-XX:+UseCMSCompactAtFullCollection	使用并发收集器时,开启对年老代的压缩
-XX:CMSFullGCsBeforeCompaction=0	上面配置开启后,这里设置多少次FullGC后,对年老代进行压缩

jvm调优总结(6):新一代的垃圾回收算法

垃圾回收的瓶颈

传统分代垃圾回收方式,已经在一定程度上把垃圾回收给应用带来的负担降到了最小,把应用的吞吐量推到了极限
但无法解决一个问题,就是Full GC所带来的应用暂停。

增量收集的演进

增量收集的方式在理论上可以解决传统分代方式带来的问题。
增量收集把堆空间划分成一系统内存块,使用时,先使用其中一部分,
垃圾收集时把之前用掉的部分中的存活的对象再放到后面没有用的空间中,
这样就实现一直边使用边收集的效果,避免了传统分代方式整个使用完了再暂停的回收的情况。

当然,传统分代收集方式也提供了并发收集,但是有致命的缺点,就是把整个堆做为一个内存块,这样一方面会造成碎片(无法压缩),
另一方面他的每次收集都是对整个堆的收集,无法进行选择,在暂停时间的控制上还是很弱。
而增量方式,通过内存空间的分块,恰恰可以解决上面问题


Garbage Firest(G1)

目标:从设计目的看G1是为了大型应用准备的
支持很大的堆
高吞吐量
-支持多CPU和垃圾回收线程
-在主线程暂停的情况下,使用并行收集
-在主线程运行的情况下,使用并发收集
实时目标:可配置在N毫秒内最多只占用M毫秒的时间进行垃圾回收

算法详解
G1吸收了增量收集优点,把整个堆划分为一个一个等大小的区域。
内存的回收和划分都是以区域为单位,
它也吸收了CMS的特点,把这个垃圾回收分为几个阶段,分散一个垃圾回收过程,
G1也认同分代垃圾回收的思想,认为不同对象的生命周期不同,可以采用不同收集方式
为了达到对回收时间的可预计性,G1在扫描区域以后,对其中的活跃对象的大小进行排序,首先会收集那些活跃对象小的区域

回收步骤

初始标记
G1对于每个区域都保存了两个标识用的bitmap,一个previous marking bitmap,一个next marking bitmap,bitmap中包含了一个bit的地址信息来指向对象的起始点。

开始Initial Marking之前,首先并发的清空next marking bitmap,然后停止所有应用线程,
并扫描出每个区域中root可直接访问到的对象,将区域中的top值放入next top at mark start中,之后恢复所有应用线程

触发这个步骤执行的条件为
G1定义了jvm heap大小的百分比的阀值,称为h(目前固定,后续可能动态),另外还有一个H,H的值为(1-h)*Heap Size。	
在分代方式下,G1还定义了一个u以及soft limit,soft limit的值为H-u*Heap Size,
当Heap中使用的内存超过了soft limit值时,就会在一次clean up执行完毕后再应用允许的GC暂停时间范围内尽快的执行此步骤

在pure方式下,G1将marking与clean up组成一个环,以便clean up能充分的使用marking的信息,当clean up开始回收时,首先回收能够带来最多内存的跨区域,
当经过多次的clean up,回收到没多少空间的区域时,G1重新初始化一个新的marking与clean up构成的环

并发标记
按照之前Inital Marking扫描到的对象进行遍历,以识别这些对象的下层对象的活跃状态,
对于在此期间应用线程并发修改的对象的依赖关系则记录到remembered set logs中,
新创建的对象则放入比top值更高的地址区间中,这些新创建的对象默认状态是活跃的,同时修改top值

最终标记暂停
当应用线程的remembered set logs未满时,是不会放入filled RS buffers中的,在这样的情况下,这些remembered set logs中记录的card的修改就会被更新了,
因此需要这一步,这一步要做的就是把应用线程中存在的remembered set logs的内容进行处理,并相应的修改remembered sets,这一步需要暂停应用,并行的运行

存活对象计算及清除
G1中,并不是说Final Marking Pause执行完了,就肯定执行clean up这步,由于这步需要暂停应用,G1为了能够达到准实时的要求,需要根据用户指定的最大的GC造成的暂停时间来合理的规划什么时候执行clean up,另外还有几种情况也是会触发这个步骤的执行

G1采用的是复制的方法来进行收集,必须保证"to space"的空间都是够的,
因此G1采用的策略当使用的内存空间达到H时,就执行clean up

JVM调优方法

jvm调优工具
Jconsole:jdk自带,功能简单
JProfiler:商业软件,需要付费,功能强大
VisualVM:jdk自带,功能强大,与JProfiler相似

如何调优
观察内存释放情况、集合类检查、对象树

调优工具一般分为以下几类功能
1、堆信息查看
可查看堆空间大小分配(年轻代,年老代,持久代)
提供即时的垃圾回收功能
垃圾监控(长时间监控回收情况)
查看堆内类、对象信息查看:数量、类型等
对象引用情况查看
2、线程监控
线程信息监控:系统线程数量
线程状态监控:各个线程都处在什么样的状态下
dump线程详细信息:查看线程内部运行情况
死锁检查
热点分析:CPU热点、内存热点
快照
3、内存泄漏检查 
内存泄漏时用完的资源没有回收引起错误。

年老代堆空间被占满
异常:java.lang.OutOfMemoryRrror:Java heap space
这是最典型的内存泄漏方式
解决方法,根据垃圾回收前后情况对比,同时根据对象引用情况分析,基本都能找到泄漏点
		
持久代被占满
异常:java.lang.OutOfMemoryRrror:PermGen space
说明Perm空间已满,无法为新的class分配存储空间而引发的异常
解决方法
1、-XX:MaxPermSize=16m
2、换用JDK,比如Jrocket

线程堆栈满
异常:Fatal:Stack size too small
说明:java中一个线程的空间大小是有限制的,JDK5.0以后的值是1M
解决:增加线程栈大小,-Xss2m。

堆栈溢出
异常:java.lang.StackOverflowError
说明:一般就是递归没返回或循环调用造成的

系统内存被占满
异常:java.lang.OutOfMemoryError:unable to create new native thread
说明:由于操作系统没有足够资源来产生这个线程造成的
解决:
1、重新设计系统减少线程数量
2、线程数量不能减少的情况下,通过-Xss减少单个线程大小。以便能生产更多的线程

jvm主要负责帮我们内存管理

什么叫调优?发挥机器本身的性能

jvm运行时数据区?

数据和指令,(共有)数据有方法区和堆,(私有)指令有程序计数器,虚拟机栈,本地方法栈
程序计数器	指向当前线程正在执行的字节码指令的地址(行号)
虚拟机栈		存储当前线程运行方法时所需要的数据、指令。返回地址
本地方法栈
方法区	类信息、常量(1.7有变化,字符串常量移到堆中)、静态变量
堆 		存放new出来的对象,数组 

反编译命令 javap -v HelloworldDemo > 0711.txt

一个方法是不是一个栈帧?一个方法要被一个线程运行,一个方法要去执行的时候,这个线程所需要这个方法的所有数据指令和返回地址,所以是

栈帧里面有局部变量表、操作数栈、动态链接、出口等

递归的时候会有多个栈帧

带native的方法叫本地方法

栈指向堆例子:局部变量有引用变量

线程安全就是保证可见性,原子性,有序性

三个代

<1.8 heap中有新生代和老年代,持久代在方法区
>=1.8 heap把永久代改成Meta Space不会内存溢出,是动态的,与ArrayList类似,

何时开始GC

Eden内存满了之后,开始Minor GC
升到老年代的对象所需空间大于剩余空间开始Full GC

垃圾回收的对象

从root开始搜索没有可达对象

垃圾回收器的类型

串行垃圾回收器
并行垃圾回收器
并发标记扫描垃圾回收器
G1垃圾回收

垃圾回收算法

引用计数法
标记清除法
复制算法
标记压缩法
分代算法
分区算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值