jvm虚拟机
一、介绍
虚拟机
系统虚拟机:VM、virtualbox
程序虚拟机:java虚拟机(hotspot)
java虚拟机主要执行Java字节码指令
二、Java虚拟机基本结构
-
类加载子系统:负责从文件系统或者网络中加载class信息,加载的信息存放在一块称为方法区的内存空间
-
方法区:存放类信息、常量信息、常量池信息,包括字符串面量和数字常量
-
java堆:在java虚拟机启动的时候建立java堆,他是java程序最主要的内存工作区,几乎所有的对象实例都存在java的堆中,堆空间是所有线程共享的。
-
直接内存:java的NIO库允许java程序使用直接内存,从而提高性能,通常直接内存速度会优于java堆,读写频繁的场合可能会考虑使用
-
java栈:每个虚拟机线程都有一个是有的栈,一个线程的java栈在线程创建的时候被创建,java栈中保存着局部变量,方法参数,同时Java的方法调用,返回值等
-
本地方法栈:本地方法栈和java的栈非常类似,本地方法栈用于本地方法调用,java虚拟机允许java直接调用本地方法栈
-
垃圾回收子系统:垃圾回收子系统是java的核心,也是必不可少的,java有自己的垃圾清理机制,开发人员无需手动清理
-
略
-
执行引擎:虚拟机最核心的组成就是执行引擎,他负责执行虚拟机的字节码,一般先编译成机器码后执行
三、java堆介绍
1、堆解决的是数据存储问题,即数据存在哪
2、栈解决的程序运行问题,即程序如何执行、如何处理数据
3、方法区则是辅助堆栈的永久区,解决堆栈信息的产生,类信息、静态信息存在方法区中
heap图
1、刚实例化的对象进入到eden区,经过gc后进入s0或s1区,其中s0和s1区大小相等,可以相互转换的空间
2、老年代产生gc的可能性不高、15gc后进入到老年代
java堆是java应用程序最密不可分的内存空间,几乎所有对象都存在其中,java堆完全自动化管理,通过垃圾回收机制,垃圾对象会自动清理,不需要显示释放。
四、java栈介绍
栈由三部分组成:局部变量表、操作数栈、帧数据区
1、局部变量表:用于报错函数的参数和局部变量表
2、操作数栈:保存计算的中间结果、变量的临时存储空间
3、帧数据区:函数返回或者出现异常时
4、方法区是一块所有线程共享的内存区域,保存类信息
定义类太多,导致方法区溢出,虚拟机会抛出内存溢出错误
五、参数设置
-
堆参数分配
-XX 配置虚拟机应用程序的,系统级别(jvm)的配置,配置日志信息
非-XX基本上是对应用层面上的配置
-XX:(+/-) +表示启用 - 表示禁用
-
参数
-XX:+printGC 虚拟机启动后,遇到GC就打印日志
-XX:+UseSerialGC 启动串行GC
-XX:+printGCDetials 查看GC详细情况,包括各分区的情况
-Xms: java程序启动时初始堆大小
-Xmx: java程序能够获得的最大堆大小
-XX:+printCommandLineFlags 输出虚拟机参数
-XlogGC:d:/gc.log 将GC日志输出成文件
-
介绍
[GC[DefNew:658k->120k]]: DefNew表示新生代,658k表示回收前,120k表示回收后
perm:永久区
Tenured :老年代
实际工作中,直接将初始堆大小与最大堆大小设置相等,好处是可以减少程序运行时的垃圾回收次数,提高性能。
-
新生代配置
-Xmn : 设置新生代大小,新生代设置比较大会减少老年代的大小,这个参数对系统的性能及GC行为有很大的影响,一般设置整个堆空间的1/3或者1/4左右
-XX:SurvivorRatio 设置新生代中eden空间和from/to空间的比例
-XX:SurvivorRatio=eden/from=eden/to
-XX:NewRatio=老年代/新生代
不同的堆分布情况,对系统的执行会产生一定的影响,应该根据系统的特点做出合理的配置。
基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。
除了可以设置新生代的绝对大小(-Xmn)还可以设置新生代与老年代的比例,-XX:NewRatio=老年代/新生代
-
堆溢出处理(OOM)
堆溢出导致内存溢出错误 OOM
-XX:HeapDumpOnOutOfMemoryError 内存溢出时导出整个堆信息
-XX:HeapDumpPath 导出堆的存放路径
内存分析工具 MemoryAnalyzer
-
栈参数
-Xss 指定线程的最大栈空间,这个参数也直接决定了函数可以调用的最大深度(一般递归时用)
-XX:MaxPermSize 最大方法区大小,设置合适值,以免出现永久区内存溢出问题
-XX:PermSize 方法区大小,默认64M
-
直接内存配置
-XX:MaxDirectMemorySize
-
jvm启动模式
client模式启动比较快,server模式启动比较慢
长期使用用server模式,1.8以后都是server模式了
六、垃圾回收
-
介绍
指内存中,不在被使用的对象
-
回收算法
引用计数法、标记压缩算法、复制算法、分代、分区
-
引用计数法:对象被引用时计数器加1,引用失效是减1,无法处理循环引用的情况,每次进行加减操作比较浪费系统资源
-
标记清除法:分为标记和清除两个阶段进行处理内存中的对象,弊端:会出现空间碎片问题,垃圾回收后的空间不是连续的,不连续的内存空间比连续的内存空间效率低。
-
复制算法:内存空间分为2块,每次只是用其中的一块,在垃圾回收时,将内存存留的对象复制到另一块,清理原内存,来回反复
-
标记压缩算法:标记压缩算法在标记清除法的基础上做了优化,把存活的对象压缩到内存的一端,而后进行垃圾清理,java老年代中使用。
-
分代算法:根据对象的特点把内存分为N块,根据每个内存的特点,使用不同的算法。
-
分区算法:将整个内存分为N多个小的独立空间,每个小空间都可以独立使用,这样细粒度的控制一次回收多少个小空间和回收那些小空间,而不是对整个空间进行GC,从而提高性能,并减少GC停顿时间
-
-
垃圾回收是的GC停顿现象
垃圾回收的任务是识别和回收垃圾对象进行内存清理,为了让垃圾回收器可以高效的执行,大部分情况下,会要求系统进入一个停顿的状态,停顿的目的是终止所有应用线程,只有这样系统才不会有新的垃圾,保证系统状态在某一瞬间的一致性,有利于更好的标记垃圾对象,因此会产生停顿现象。
-
新生代、老年代特点
新生代回收的频率很高,但是每次回收耗时都很短,新生代对象死的快,用复制算法
老年代回收频率低,耗时会相对较长,尽量减少老年代GC,使用标记压缩算法
根据设置 -XX:MaxTenuringThreshold 参数,可以指定新生代对象经过多少次回收 后进入老年代。
大对象(新生代eden区无法装入时,也会直接进入老年代)jvm里有一个参数设置对象的大小超过指定的大小后直接进入到老年代。-XX:PretenureSizeThreshold
-
TLAB区
1、tlab区优先分配空间,tlab区是逻辑概念
2、tlab即线程本地分配缓存,为加速对象分配而生的,每个线程都会产生一个tlab,可以避免多线程冲突问题,当大对象无法再tlab分配时,则会直接分配到堆上。
-
TLAB参数
-XX:+UseTLAB 启用tlab
-XX:_TLABSize 设置tlab大小
-XX:TLABRefillWasteFraction 设置tlab空间单个对象大小,他是一个比值,默认是64,如果对象大于整个空间的1/61 ,则在堆创建对象。
-XX:PrintTLAB 查看tlab信息
-XX:ResizeTLAB 自调整TLABREfillWasteFraction阈值
-
对象创建流程
对象的创建,根据数据大小,参数的设置,决定如何创建和分配
七、垃圾回收器
1、介绍
串行垃圾回收器、并行垃圾回收器、CMS回收器、G1回收器
2、串行垃圾回收器
-
串行回收器可以在新生代和老年代使用
-
串行回收器用于单线程垃圾回收的回收器,只有一个工作线程,专注性和独占性比较好 使用-XX:+UseSerialGC 设置新生代和老年代串行回收器
3、并行垃圾回收器(parNew)
-
parNew 工作在新生代的垃圾回收器,只是简单的将串行回收器多线程化,回收策略和串行一样
-
-XX:UseParNewGC 新生代parNew回收器,老年代使用串行回收器
-
-XX:parallelGCThreads 设置垃圾回收器的线程数,最好和计算机的CPU相当
4、并行垃圾回收器(parallelGC)
-
新生代parallelGC回收器,使用复制算法,多线程独占式,非常关注系统的吞吐量
-
两个参数控制系统的吞吐量 -XX:MaxGCPauseMillis 设置最大垃圾回收停顿时间,如果希望减少GC停顿时间,可以将 MaxGCPauseMillis设置很小,这样会导致GC频繁,增加GC总时间,降低吞吐量 -XX:GCTimeRatio 吞吐量大小,一个在1-100之间的数,默认是99,系统将花费不超过1/(1+n)的时间用于垃圾回收,就是1/(1+99)=1%的时间。 -XX:+UseAdaptiveSizePolily 打开自适应模式,自动调整,在吞吐量和停顿时间之间找到平衡点
5、并行回收器(parallelOldGC)
-
老年代parallelOldGC回收器,多线程回收器,使用标记压缩算法,关注吞吐量
-XX:+UseParallelOldGC 启用老年代并行垃圾回收器 -XX:+parallelGCThreads 设置回收器线程数
6、CMS回收器
-
CMS使用标记清除算法,主要关注系统停顿时间 -XX:+UseConcMarkSweepGC 启用CMS垃圾回收器 -XX:+ConcGCThreads 设置线程数
-
CMS不是独占的回收器,CMS回收过程中,应用程序仍然在不停的工作,又会产生新的垃圾,使用CMS要确保应用程序的内存足够可用,CMS不会等到应用程序饱和的时候才去回收垃圾,而是在某一阈值时开始回收,-XX:CMSInitiatingOccupancyFraction来指定,默认是68,老年代空间使用率达到68的时候,会执行CMS回收,如果内存增长的太快,CMS会出现内存不足的情况,此时CMS会失败,虚拟机将启动老年代串行回收器进行垃圾回收,导致程序中断,直到垃圾回收完成后才会正常运行,GC停顿时间较长。
-
标记清除算法存在内存碎片问题 CMS有个参数 -XX:UseCmsCompactAtFullCollection 可以使CMS回收之后进行一次碎片整理 -XX:CMSFullGCsBeforCompaction 设置进行都少次CMS回收之后,对内存进行一次压缩
7、G1回收器
-
G1属于分代垃圾回收器,区分新生代和老年代,依然有eden和from/to区,他不要求整个eden区或者新生代、老年代的空间连续,使用分区算法。 并行性:多线程同时执行。 并发性:G1拥有与应用程序交替执行的能力部分工作可与应用程序同时进行,在整个GC期间不会完全阻塞应用程序。
分代GC:G1是一个分代的收集器,兼顾新生代和老年代
空间整理:G1在垃圾回收过程中,不会像CMS那样在若干次GC之后进行碎片整理,G1采用有效复制对象的方式,减少空间碎片。
-XX:UseG1GC 应用G1收集器
-XX:parallelGCThreads 并行回收的线程数
gc次数少,性能就高一些
八、测试工具
Apache Jmeter 通过Jmeter对Tomcat增加压力
观察不同的配置参数对吞吐量的影响(停顿时间,内存的使用情况,回收的效率)
Jproject
九、负载均衡
-
IP级别负载均衡,入口宽一些,LVS更改了底层的IP。
-
反向代理实现负载均衡
备注:集群在故障切换时,连接不是立即被回收,所以要判断 !connection.isOpen()