Java是一门面向物件程式语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指标等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向物件程式语言的代表,极好地实现了面向物件理论,允许程序员以优雅的思维方式进行复杂的程式设计。
一、虚拟机器内存图解
JAVA程式执行与虚拟机器之上,执行时需要内存空间。虚拟机器执行JAVA程式的过程中会把它管理的内存划分为不同的资料区域方便管理。虚拟机器管理内存资料区域划分如下图:
(一)、资料区域分类:
方法区: (Method Area)
虚拟机器栈: (VM Stack)
本地方法栈 :(Native Method Stack)
堆: (Heap)
程式计数器 :(Program Counter Register)
直接内存: (Direct Memory)
(二)、说明:
1、程式计数器
行号指示器,字节码指令的分支、循环、跳转、异常处理、执行绪恢复(CPU切换),每条执行绪都需要一个独立的计数器,执行绪私有内存互不影响,该区域不会发生内存溢位异常。
2、虚拟机器栈
是执行绪私有的,宣告周期与执行绪相同,虚拟机器栈是Java方法执行的内存模型,每个方法被执行时都会建立一个栈帧,即方法执行期间的基础资料结构,栈帧用于储存:区域性变量表、算子栈、动态连结、方法出口等,每个方法执行中都对应虚拟机器栈帧从入栈到处栈的过程。
是一种资料结构,是虚拟机器中的区域性变量表,对应物理层之上的程式资料模型。
区域性变量表,是一种程式执行资料模型,存放了编译期可知的各种资料型别例如:
Boolean、byte、char、short、int、float、long、double、物件引用型别(物件内存地址变数,指标或控制代码),程式执行时,根据区域性变量表分配栈帧空间大小,在执行中,大小是不变的异常型别:stackOverFlowError 执行绪请求栈深度大于虚拟机器允许深度 OutOfMemory 内存空间耗尽无法进行扩充套件。
3、 本地方法栈
与虚拟机器栈类似,虚拟机器栈为Java程式服务,本地方法栈支援虚拟机器的执行服务,具体实现由虚拟机器厂商决定,也会丢掷 stackOverFlowError、OutOfMemory异常。
4、堆
是虚拟机器管理内存中最大的一部分,被所有执行绪共享,用于存放物件例项(物件、阵列),物理上不连续的内存空间,由于GC收集器,分代收集,所以划分为:新生代 Eden、From SurVivor空间、To SurVivor空间,allot buffer(分配空间),可能会划分出多个执行绪私有的缓冲区,老年代。
5、方法区
与堆一样属于执行绪共享的内存区域,用于储存虚拟机器载入的类资讯、常量、静态变数、即时编译器编译后的程式码(动态载入OSGI)等资料。理论上属于java虚拟机器的一部分,为了区分开来叫做 Non-Heap非堆。
这个区域可以选择不进行垃圾回收,该区域回收目的主要是常量池的回收,及型别的解除安装class,内存区不足时会丢掷OutOfMemory异常
执行时常量池:
方法区的一部分,Class的版本、字段、界面、方法等,及编译期生成的各种字面量、符号引用,编译类载入后存放在该区域。会丢掷OutOfMemory异常。
6、直接内存
直接内存不属于虚拟内存区域,是一种基于通道与缓冲区的IO方式,可以使用本地函式直接分配堆外内存,在堆中储存引用的外部内存地址,通过引用完成对直接引用内存的操作,1.4之后提供的NIO显著提高效率,避免了堆内存与Native内存的来回复制操作,不受虚拟机器内存控制,会丢掷OUtOfMemory异常。
二、物件访问内部实现过程
物件访问 涉及到物件的地址变更状态变更,内存地址移动,变数、界面、实现类、方法、父型别等。(一)、 控制代码方式 (访问)
(二)、指标方式 (访问)
优缺点:
控制代码访问方式:reference中储存的是稳定的地址,物件变更时只会改变控制代码例项资料指标,引用本身不需要修改
指标访问方式:优点速度快,节省了指标定位时间开销
三、内存区域控制引数及对应溢位异常
开发过程中,或程式执行过程中每次遇到OutOfMemory异常或GC异常或StackOverflowError异常我们都是一堆引数乱配,都把值调大,只是大体知道是跟jvm内存分配有关,具体应该怎么调,对应的异常应该调整那些引数,或者换句话说,jvm内存分配区域中都分别对应那些引数大多数情况下都是不知道的,只是把相关的引数跳上去,预期结果都是应该起作用,到底能不能起作用,自己心里也没底。下面就来说一下jvm堆、栈、方法区等内存区域对应的引数,及每个区域可能丢掷的异常型别,发生异常的场景分析。
(一)、引数型别
1、堆空间引数
2、栈空间引数
3、方法区空间引数
4、本机直接内存引数
(二)、异常型别
1、OutOfMemory异常
2、StackOverflowError异常
(三)、辅助引数说明
1、-XX:+HeapDumpOnOutOfMemoryError 打印堆内存异常时打印出快照资讯
2、-XX:+HeapDumpPath 快照输出路径
3、-Xmn指定eden区的大小 -XX:SurvirorRation来调整幸存区的大小
4、-XX:PretenureSizeThreshold设定进入老年代的阀值
(四)、引数说明、对应场景的异常
1、堆内存引数
-Xms:堆最小值(新生代和老年代之和)
-Xmx:堆最大值(新生代和老年代之和)
当最小值=最大值时,这时堆内存是不可扩充套件的。
例:-Xms80M -Xmx80M
通常将-Xmx和-Xms设定为一样的大小来减少gc的次数,堆内存不足时丢掷OutOfMemoryError异常。
2、栈内存引数
-Xss
例:-Xss128k
单执行绪下无论栈帧太大还是栈容量太小,及引用深度超过虚拟机器允许深度都会丢掷StackOverflowError每个方法压入栈的帧大小是不一致的。多执行绪下当每个执行绪分配栈帧太大内存不能够扩充套件时丢掷OutOfMemoryError异常执行绪栈帧越大,可建立的执行绪越少。
3、方法区引数
-XX:PermSize方法区内存最小值
-XX:MaxPermSize 方法区内存最大值
各个执行绪共享的内存区域,主要用来储存类的元资料、常量、静态变数、即时编译器编译后的程式码等资料
例:-XX:PermSize=20M -XX:MaxPermSize=20M
异常型别 OutOfMemoryError :
原因:常量过多,或代理反射等使用频繁
4、本机直接内存引数
-XX:MaxDirectMemorySize
例:-XX:MaxDirectMemorySize=10M
不足时丢掷OutOfMemory异常
四、垃圾收集算法
经典的垃圾回收算法以下几种(一)、标记--清除算法(Mark-Sweep)
回收前状态:
回收后状态:
优缺点:
算法执行分为两个阶段标记与清除,所有的回收算法,基本都基于标记回收算法做了深度优化
缺点:效率问题,内存空间碎片(不连续的空间)
(二)、复制算法(Copying)
回收前状态:
Eden内存空间 8
Survivor1空间(From空间)1
Survivor2空间(To空间) 1
Eden内存空间与Survivor空间 8:1
回收后状态:
Survivor1空间(From空间)1
Eden内存空间与Survivor空间 8:1
优缺点:比较标记清除算法,避免了回收造成的内存碎片问题,
缺点:以区域性的内存空间牺牲为代价,不过空间的浪费比较小,预设8:1的比例1是浪费的。
复制也有一定的效率与空间成本
(三)、标记整理算法(Mark-Compact)
回收前状态:
回收后状态:
优缺点:避免了,空间的浪费,与内存碎片问题。
缺点:整理时复制有效率成本。
五、垃圾收集器
(一)、七种垃圾收集器1、Serial(序列GC)-XX:+UseSerialGC
2、ParNew(并行GC)-XX:+UseParNewGC
3、Parallel Scavenge(并行回收GC)
4、Serial Old(MSC)(序列GC)-XX:+UseSerialGC
5、 CMS(并发GC)-XX:+UseConcMarkSweepGC
6、 Parallel Old(并行GC)-XX:+UseParallelOldGC
7、G1(JDK1.7update14才可以正式商用)
(二)、1~3用于年轻代垃圾回收:年轻代的垃圾回收称为minor GC
(三)、4~6用于年老代垃圾回收(当然也可以用于方法区的回收):年老代的垃圾回收称为major GC
G1独立完成"分代垃圾回收"
注意:并行与并发
并行:多条垃圾回收执行绪同时操作
并发:垃圾回收执行绪与使用者执行绪一起操作
(四)、常用五种组合
Serial/Serial Old
ParNew/Serial Old:与上边相比,只是比年轻代多了多执行绪垃圾回收而已
ParNew/CMS:当下比较高效的组合
Parallel Scavenge/Parallel Old:自动管理的组合
G1:最先进的收集器,但是需要JDK1.7update14以上
(五)、 Serial/Serial Old
年轻代Serial收集器采用单个GC执行绪实现"复制"算法(包括扫描、复制)
年老代Serial Old收集器采用单个GC执行绪实现"标记-整理"算法
Serial与Serial Old都会暂停所有使用者执行绪(即STW)
说明:
STW(stop the world):编译程式码时为每一个方法注入safepoint(方法中循环结束的点、方法执行结束的点),在暂停应用时,需要等待所有的使用者执行绪进入safepoint,之后暂停所有执行绪,然后进行垃圾回收。
适用场合:
CPU核数
-XX:UseSerialGC:强制使用该GC组合
-XX:PrintGCApplicationStoppedTime:检视STW时间
(六)、ParNew/Serial Old:
ParNew除了采用多GC执行绪来实现复制算法以外,其他都与Serial一样,但是此组合中的Serial Old又是一个单GC执行绪,所以该组合是一个比较尴尬的组合,在单CPU情况下没有Serial/Serial Old速度快(因为ParNew多执行绪需要切换),在多CPU情况下又没有之后的三种组合快(因为Serial Old是单GC执行绪),所以使用其实不多。
-XX:ParallelGCThreads:指定ParNew GC执行绪的数量,预设与CPU核数相同,该引数在于CMS GC组合时,也可能会用到
(七)、Parallel Scavenge/Parallel Old:
特点:
年轻代Parallel Scavenge收集器采用多个GC执行绪实现"复制"算法(包括扫描、复制)年老代Parallel Old收集器采用多个GC执行绪实现"标记-整理"算ParallelScavenge与Parallel Old都会暂停所有使用者执行绪(即STW)
说明:
吞吐量:CPU执行程式码时间/(CPU执行程式码时间+GC时间)CMS主要注重STW的缩短(该时间越短,使用者体验越好,所以主要用于处理很多的互动任务的情况)Parallel Scavenge/Parallel Old主要注重吞吐量(吞吐量越大,说明CPU利用率越高,所以主要用于处理很多的CPU计算任务而使用者互动任务较少的情况)
引数设定:
-XX:+UseParallelOldGC:使用该GC组合
-XX:GCTimeRatio:直接设定吞吐量大小,假设设为19,则允许的最大GC时间占总时间的1/(1+19),预设值为99,即1/(1+99)
-XX:MaxGCPauseMillis:最大GC停顿时间,该引数并非越小越好
-XX:+UseAdaptiveSizePolicy:开启该引数,-Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold这些引数就不起作用了,虚拟机器会自动收集监控资讯,动态调整这些引数以提供最合适的的停顿时间或者最大的吞吐量(GC自适应调节策略),而我们需要设定的就是-Xmx,-XX:+UseParallelOldGC或-XX:GCTimeRatio两个引数就好(当然-Xms也指定上与-Xmx相同就好)
注意:
-XX:GCTimeRatio和-XX:MaxGCPauseMillis设定一个就好
不开启-XX:+UseAdaptiveSizePolicy,-Xmn/-XX:SurvivorRatio/-XX:PretenureSizeThreshold这些引数依旧可以配置,以resin服务器为例
-Xms2048m-Xmx2048m-Xmn512m-Xss1m-XX:PermSize=256M-XX:MaxPermSize=256M-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=15-XX:+UseParallelOldGC-XX:GCTimeRatio=19-XX:+PrintGCDetails-XX:+PrintGCTimeStamps View Code
适用场合:
很多的CPU计算任务而使用者互动任务较少的情况不想自己去过多的关注GC引数,想让虚拟机器自己进行调优工作
(八)、调优方法
1、 新物件预留新生代
由于fullGC(老年代)的成本远比minorGC(新生代和老年代)的成本大,所以给应用分配一个合理的新生代空间,尽量将物件分配到新生代减小fullGC的频率
2、 大物件进入老年代
将大物件直接分配到老年代,保持新生代物件的结构的完整性,以提高GC效率, 以通过-XX:PretenureSizeThreshold设定进入老年代的阀值
3、 稳定与震荡的堆大小
稳定的对大小是对垃圾回收有利的,方法将-Xms和-Xmx的大小一致
4、 吞吐量优先
尽可能减少系统执行垃圾回收的总时间,故采用并行垃圾回收器
-XX:+UseParallelGC或使用-XX:+UseParallelOldGC
5、 降低停顿
使用CMS回收器,同时减少fullGC的次数
(九)、获取gc资讯的方法
1 、-verbose: gc或者-XX:+PrintGC 获取gc资讯
2 、-XX:+PrintGCDetails 获取更加详细的gc资讯
3、 -XX: +PrintGCTimeStamps 获取GC的频率和间隔
4 、-XX:+PrintHeapAtGC 获取堆的使用情况
5 、-Xloggc:D: gc.log 指定日志情况的储存路径
(十)、jvm调优实战-tomcat启动加速
在tomcat的bin/catalina.bat档案的开头新增相关的配置
六、监控工具
监控工具:一般问题定位,效能调优都会使用到。(一)、jps
Jps是参照Unix系统的取名规则命名的,而他的功能和ps的功能类似,可以列举正在执行的饿虚拟机器程序并显示虚拟机器执行的主类以及这些程序的唯一ID(LVMID,对应本机来说和PID相同),他的用法如下:
Jps [option] [hostid]
jps -q 只输出LVMID
jps -m 输出JVM启动时传给主类的方法
jps -l 输出主类的全名,如果是Jar则输出jar的路径
jps -v 输出JVM的启动引数
(二)、jstat
jstat主要用于监控虚拟机器的各种执行状态资讯,如类的装载、内存、垃圾回收、JIT编译器等,在没有GUI的服务器上,这款工具是首选的一款监控工具。其用法如下:
jstat [option vmid [interval [s|ms] [vount] ] ]
jstat 监控内容 执行绪好 重新整理时间间隔 次数
jstat –gc 20445 1 20 :监视Java堆,包含eden、2个survivor区、old区和永久带区域的容量、已用空间、GC时间合计等资讯
jstat –gcutil 20445 1 20:监视内容与-gc相同,但输出主要关注已使用空间占总空间的百分比
jstat –class 20445 1 20:监视类的装载、解除安装数量以及类的装载总空间和耗费时间等
.......-gccapcity......:监视内容与-gc相同,但输出主要关注Java区域用到的最大和最小空间
.......-gccause........:与-gcutil输出资讯相同,额外输出导致上次GC产生的原因
.......-gcnew..........:监控新生代的GC情况
.......-gcnewcapacity..:与-gcnew监控资讯相同,输出主要关注使用到的最大和最小空间
.......-gcold..........:监控老生代的GC情况
.......-gcoldcapacity..:与-gcold监控资讯相同,输出主要关注使用到的最大和最小空间
.......-gcpermcapacity.:输出永久带用到的最大和最小空间
.......-compiler.......:输出JIT编译器编译过的方法、耗时资讯
.......-printcompilation:输出已经被JIT编译的方法
(三)、jinfo
jinfo的作用是实时检视虚拟机器的各项引数资讯jps –v可以检视虚拟机器在启动时被显式指定的引数资讯,但是如果你想知道预设的一些引数资讯呢?除了去查询对应的资料以外,jinfo就显得很重要了。jinfo的用法如下:Jinfo [option] pid
(四)、jmap
map用于生成堆快照(heapdump)。当然我们有很多方法可以取到对应的dump资讯,如我们通过JVM启动时加入启动引数 –XX:HeapDumpOnOutOfMemoryError引数,可以让JVM在出现内存溢位错误的时候自动生成dump档案,亦可以通过-XX:HeapDumpOnCtrlBreak引数,在执行时使用ctrl+break按键生成dump档案,当然我们也可以使用kill -3 pid的方式去恐吓JVM生成dump档案。Jmap的作用不仅仅是为了获取dump档案,还可以用于查询finalize执行伫列、Java堆和永久带的详细资讯,如空间使用率、垃圾回收器等。其执行格式下:Jmap [option] vmip
监控堆叠资讯主要用来定位问题的原因,生成堆叠快照
.......-dump......:生成对应的dump资讯,用法为-dump:[live,]format=b,file={fileName}
.......-finalizerinfo......:显示在F-Queue中等待的Finalizer方法的物件(只在linux下生效)
.......-heap......:显示堆的详细资讯、垃圾回收器资讯、引数配置、分代详情等
.......-histo......:显示堆叠中的物件的统计资讯,包含类、例项数量和合计容量
.......-permstat......:以ClassLoder为统计口径显示永久带的内存状态
.......-F......:虚拟机器对-dump无响应时可使用这个选项强制生成dump快照
例子:jmap -dump:format=b,file=yhj.dump 20445
(五)、jstack
Jstack用于JVM当前时刻的执行绪快照,又称threaddump档案,它是JVM当前每一条执行绪正在执行的堆叠资讯的集合。生成执行绪快照的主要目的是为了定位执行绪出现长时间停顿的原因,如执行绪死锁、死循环、请求外部时长过长导致执行绪停顿的原因。通过jstack我们就可以知道哪些程序在后台做些什么?在等待什么资源等!其执行格式如下:
Jstack [option] vmid
-F 当正常输出的请求不响应时强制输出执行绪堆叠
-l 除堆叠资讯外,显示关于锁的附加资讯
-m 显示native方法的堆叠资讯
(六)、jconsole
在JDK的bin目录下,监控内存,thread,堆叠等
(七)、jprofile
发现和修复内存泄漏
如何学习?有没有免费资料,今天免费分享