一、JVM引言
jvm(JAVA Virual Machine)在整个jdk中处于最底层,是负责与操作系统进行交互,用来屏蔽操作系统的差异,提供一个完成的Java运行环境,因此也叫java虚拟机。操作系统装入JVM是通过JDK中的Java.exe来完成的
1、jdk(java开发工具包)与jre(java运行环境)的区别
1)java developer kit
2)java runtime Environment 提供了调试工具包
二、JVM内存结构
三、JVM内存详解(四大部分)
类加载(.class 文件: 成员变量,方法,参数 局部变量,静态成员)
1)方法区(Method Area) :常量 类信息 字段 接口 描述信息
别名:永久代(permanent generation) 用于存储虚拟机加载的类信息,常量,静态变量,是各个线程共享的内存区域
运行时常量池:方法区的一部分,.class文件中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息就是常量池,用于存放编译器生成的各种符号引用,这部分内容将在类加载后方法方法区的运行常量池中
常量池:
byte -128—127
String 常量池
2)栈 (Stack 独立) :记录当前java执行方法的内存模型 线程安全 栈溢出
描述的是java方法执行的内存模型,每个方法被执行的时候,都会创建一个“栈帧”用于存储局部变量(包括参数),操作栈,方法出口等信息;
每个方法从被调用到执行完成的过程,就对应这一个栈帧在虚拟机中从入栈到出栈的过程。生命周期与线程相同,是线程私有的,
局部变量表: 存放着八种基本数据类型,对象引用,其中64位长度的 long 和 double 类型的数据会占用两个局部变量的空间,其余数据只占一个
局部变量表是在编译时完成分配的,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量是完全确定的,在运行期间不再改变
a.虚拟机栈
b.方法栈、
3)堆 (heap共享) 最大的内存空间 垃圾堆
java堆,GC堆(垃圾回收堆)。是JVM中所管理的内存中最大的一块内存区域,是线程共享的,在JVM启动时创建,存放了对象的实例,数组和集合(包括所有new的对象)
GC: 垃圾回收会导致整个jvm可能会出现暂时运行或是挂起的状态 会暂停对外处理程序 调优最理想就是不发生任何垃圾回收
垃圾回收分类:
GC: 一次清理指定垃圾 由jvm指定
FULL GC: 强制清空所有垃圾
手动调用垃圾回收方法:Runtime.gc(); System.gc();
JVM的优化也可以称为堆的优化和方法区的优化
4)程序计数器 字节码行号指示器
是最小的一块内存,他的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型中。
字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,异常处理。
线程恢复等基础功能都需要依赖计数器完成
JDK (6,7,8)使用的JVM版本
Java HotSpot™ 64-Bit Server VM (build 25.181-b13, mixed mode)
主要优化方法区和堆区(GC和FULL GC的调用次数)
四、JVM的代的划分
1、永久代 (Permanent Generation) 方法区
2、年轻代(Young Generation)
3、老年代(Old Generation)
4、堆(Heap) = 年轻代(Eden + Survivorl + Survivor2) + 老年代
Heap(堆)主要构成:
年轻代(新生代)主要构成 10份:
Eden: 8份 内容:
新创建的对象
当发生垃圾回收时会将存活的对象放入s1或s2
Survivorl: 1份
Survivorl2: 1份
当多次垃圾回收之后任然存活的对象或者s1或s2 占满时 任然存活的对象 放入老年代
老年代(年老代)主要构成:
**内存溢出** 解决办法 :加扩大内存空间 或 重启服务器清内存
优化 新生代(扩大Eden的内存空间) 和 老年代 的内存空间
堆空间大小 永久代空间大小 以及 年轻代中 的比例问题
五、触发GC和FULLGC
GC: 当new新对象时,并且在Eden申请空间失败是 会触发GC
FULLGC 触发条件:
老年代(Old) 被写满
永久代(Perm)被写满
System.gc()被显式调用
六、优化参数设置
查看JVM运行情况命令 : jvisualvm (JDK提供的工具包)
- 堆设置
-Xms128m: 初始堆大小
当在多次调大堆内存后 在日志文件中还能发现 GC和 FullGC 时就是永久代需要优化了(方法区)
-Xmx256m: 最大堆大小
-XX:NewRatio=n: 设置年轻代和老年代的比重,(如:3,代表年轻代和老年代比值为 1:3,年轻代占老年代的1/4)
-XX:SurvivorRatio=n: 年轻代中Eden区与两个Survivor的比值(如:3 代表 Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5)
-XX:MaxPermSize=512m : 设置永久代的大小 - 收集器设置
-XX:+UseSerialGC: 设置串行收集器
-XX:+UseParallelGC: 设置并行收集器
-XX:+UseParalledlOldGC:设置并行老年代收集器
-XX:+UseConcMarkSweepGC: 设置并发收集器
-XX:+UseG1GC:设置并发收集器 - 垃圾回收统计信息
-verbose:gc
-XX:+PrintGC : 垃圾回收
-XX:+PrintGCDetails :垃圾回收详情
-XX:+PrintGCTimeStamps : 垃圾回收的时间戳
-Xloggc:filename :日志文件存放路径 - 并行收集器设置(设置垃圾收集器)
-XX:PrarllelGCThreads=n: 设置并行收集器收集时使用的CPU数,并行收集线程数
-XX:MaxGCPauseMillis=n: 设置并行收集最大暂停时间
-XX:GCTimeRatio=n: 设置垃圾回收时间占程序运行时间的百分比(1/(1+n)) - 并发收集器设置
-XX:+CMSIncrementalMode: 设置增量模式,适用于单CPU情况
-XX:ParallelGCTreads=n : 设置并发收集器年轻代收集方式为并行收集时,使用的CPU数,并行收集线程数
七、优化工具的启动虚拟机
在工具启动目录下 修改对应的 配置文件
MyEclipse: .ini 文件
IDEA: .vmoptions 文件
查看对应生成的jvm.log文件 响应的对配置文件参数进行修改:
设置堆内存最小空间
设置堆内存最大空间
设置永久代空间
设置年轻代和老年代大小比例(推荐使用默认)
设置年轻代 Eden和S1,S2比例(推荐使用默认)
八、Tomcat Jvm优化
1.Windows系统中修改环境变量
变量名: JAVA_OPTS
变量值: -verbose:gc -Xms1400m -Xmx1400m -XX:PermSize=400m -XX:MaxPermSize=400m -XX:PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:D:\jvm.log
2.Linux中设置
在/etc/profile中加入:
Export JAVA_OPTS=-verbose:gc -Xms1400m -Xmx1400m -XX:PermSize=400m -XX:MaxPermSize=400m -XX:PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:D:\jvm.log
九、垃圾回收算法
1, Mark-Sweep(标记-清除)
最基础的垃圾回收算法,其他算法都是基于这种思想。标记-清除算法分为"标记","清除"两个阶段:首先标记需要回收的对象,标记完成后统一清除对象
缺点:
1.标记和清除的效率不高
2.标记后会产生大量不连续的内存碎片
2、Copying(复制)算法
它将内存分为两块,每次只使用其中一块,当该内存满后,将存活对象复制到另一块上,然后将以使用的内存空间一次性清理掉
优点:
1.不会产生内存碎片
2.只要移动堆顶的指针,按顺序分配内存即可,简单实现,效率高
缺点:
内存缩小到原来的一半
3.Mark-Compact(标记-整理)算法
标记操作和"标记-清除"算法一样,后续操作为在清理无用对象时完成让所有存活对象向一端移动,并更新对象指针
优点: 不会产生内存碎片
缺点: 在"标记-清除"基础上还要进行对象移动,成本较高
- Cenerational Collection(分代收集) 算法(重点)
目前大部分JVM垃圾收集器采用的算法,他的核心思想是根据对象的存活的生命周期将内存划分为若干个不同区域。一般情况将堆区域划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同的特点采取最适合的收集算法,
目前大部分垃圾收集器对于新生代都采取 Copying 算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是时候需要复制的操作次数较少,但是实际不是按照 1:1 的比例来划分新生代空间的,一般来说将新生代划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中的一块 Survivor 空间,当进行回收时,将 Eden 和 Surivor 中还存活的对象复制到 另一块 Survivor 空间中,然后清理掉 Eden 和 刚才使用的 Survivor 空间,而由于老年代的特点是每次回收都只回收少量对象,一般使用 Mark-Compact 算法
注意,在堆区之外还有一个代就是永久代(Permanet Generation), 它用来存储class类、常量
方法描述等。对永久代的回收主要回收两部分: 废弃常量和无用类
十、垃圾回收器
1、Parallel Scavenge (并行收集器):不暂停线程
Parallel Scavenge 收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程。其采用的是 Copying 算法,它主要是为了达到一个可控的吞吐量
2、Parallel Old (年老代并行收集器):
Parallel Old 是 Parallel Scavnge 收集器的老年代版本(并行收集器) 使用多线程和 Mark-Compact算法
3、CMS (并发收集器) jdk 7 多使用
CMS (Current Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep 算法
4、G1 jdk 8 多使用
G1 收集器是当今收集器计算发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU,多核环境。因此它是一款并行并发收集器,并且它能建立可预测的停顿时间模型