深入理解JVM笔记

java虚拟机

  • 本地方法区
  • 方法区
  • 栈区
  • 堆区
  • pc

jvm 有哪些内存空间

  • 永久区(产生大量的类,元数据保存在此)
  • 线程栈
  • 直接内存
  • codecache

一些概念

  • GC的对象是堆空间和永久区

内存模型

  • 每一个线程有一个工作内存和内存独立
  • 工作内存存放变量的拷贝

可见性

概念:一个线程修改了变量,其他线程可以立即知道

  • volatile
  • synchronized
  • final

有序性

  • 在本线程内,操作都是有序的
  • 在线程外,操作是无序的

指令重排

  • 一个线程内保证语义的串行性
  • 编译器不考虑多线程之间的语义

常用的配置参数

Trace跟踪参数

-verbose:gc
-XX:+printGC
-XX:+printGCDetails         //打印GC的详细信息
-XX:+printGCTimeStamps      //打印GC发生的时间戳
-Xloggc:./log/gc.log        //指定GC位置
-XX:+PrintHeapAtGC          //在每一次GC之后,都打印堆的信息
-XX:+TraceClassLoading      //跟踪每一个类的加载
-XX:+PrintClassHistogram    //打印类的直方图,即使用情况

//堆的分配参数
-Xmx                    //最大堆 
-Xms                    //最小堆
-Xmn                    //新生代大小
-XX:NewRatio            //设置新生代:老年代的比 = 1:x
-XX:SurvivorRatio       //两个Survivor:eden = 2:x

-XX:+HeapDumpOnOutOfMemoryError     //OOM时导出堆文件
-XX:+HeapDumpPath                   //OOM时的路径
-XX:OnOutOfMemoryError              //OOM时,执行指定的一个脚本

//永久区
-XX:PermSize            //初始永久区
-XX:MaxPermSize         //最大永久区

//栈大小分配
-Xss                    //决定函数调用深度,每个线程都有独立的栈空间,一般都是几百K

//例子
-Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=~/

垃圾回收的一些算法

引用计数法

  • 根对象

引用计数法的问题

  • 计数+-法,影响性能
  • 循环引用:根对象不可达对象(即垃圾对象)之间循环引用

标记-清除法

  • 现代垃圾回收算法的思想基础
  • 标记阶段
  • 清除阶段

标记-压缩算法

  • 不适合存活对象较多的场合,如老年代??
  • 标记-清除算法上做了一些优化
  • 标记阶段
  • 复制压缩,清理边界外所有空间

复制算法

  • 相比于标记-清除算法,较为高效
  • 不适合于存活对象较多的场合,如老年代
  • 将原有的内存空间分为两块,每次只使用其中的一块,在垃圾回收的时候,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收
  • 缺点:每次只能使用一半的空间

复制算法扩展

  • 老年代没有关系,作为担保空间
  • 三块空间 1 + 2
    • eden
    • from
    • to
  • 大对象直接进入担保空间
  • 老年对象进入老年代
  • 剩余对象复制到

分代思想

  • 少量对象存活,适合复制算法
  • 大量对象存活,适合标记清理或者标记压缩
  • 老年代
    • 标记清除
    • 标记压缩
  • 新生代
    • 复制算法

可触及性

可触及的

  • 从根节点可以触及到这个对象
  • finalize()方法只会被调用一次,避免使用该方法

可复活的

  • 一旦所有引用被释放,就是可复活状态
  • 因为在finalize()中可能复活该对象

不可触及的

  • 在finalize()后,可能会进入不可触及状态
  • 不可触及的对象不可能复活
  • 可以回收

  • 栈中引用的对象
  • 方法区中静态成员或者常量引用的对象(全局对象)
  • JNI方法栈中引用对象

Stop the world

  • java中一种全局暂停的现象
  • 全局停顿,所有java代码停止,native代码可以执行,但是不能和JVM交互
  • 多半由于GC引起
    • Dump线程
    • 死锁检查
    • 堆Dump

GC参数

堆细分

  • eden
  • from
  • to
  • tenured

串行收齐器

  • 最老,最稳定
  • 效率高
  • 可能会产生较长的停顿
  • -XX:+UseSerialGC
    • 新生代、老年代全部使用串行回收
    • 新生代复制算法
    • 老年代标记-压缩

并行收集器ParNew

  • -XX:+UseParNewGC
    • 新生代并行
    • 老年代串行
  • Serial收齐器新生代的并行版本
  • 复制算法
  • 多线程,需要多核支持
  • -XX:ParallelGCThreads限制线程数量
  • 多线程不一定快

Parallel收集器:新生代

  • 类似ParNew
  • 新生代复制算法
  • 老年代标记压缩
  • 更加关注吞吐量
  • -XX:+UseParallelGC
    • 使用Parallel收集器 + 老年代串行
  • -XX:+UseParallelOldGC
    • 使用Parallel收集器 + 老年代并行

并行回收器参数

  • -XX:MaxGCPauseMills
    • 最大停顿时间,单位毫秒
    • GC尽量保证回收时间不超过设定值
  • -XX:GCTimeRatio
    • 0-100的取值范围
    • 垃圾收集时间占总时间的比
    • 默认99,即最大允许1%时间做GC

CMS收集器(老年代)

  • Concurrent Mark Sweep 并发标记清除
  • 标记清除算法
  • 与标记-压缩相比
  • 并发阶段会降低吞吐量
  • 老年代收集器(新生代使用ParNew)
  • -XX:+UseConcMarkSweepGC

运行过程

  • 初始标记(暂停)

    • 根可以直接关联到的对象
    • 速度快
  • 并发标记(和用户线程一起)

    • 主要标记过程,标记全部对象
  • 重新标记(暂停)

    • 由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
  • 并发清除(和用户线程一起)

    • 基于标记结果,直接清理对象

特点

  • 尽可能降低停顿
  • 会影响系统整体吞吐量和性能
  • 清理不彻底
  • 因为和用户线程一起运行,不能在空间快满的时候再清理(在垃圾回收的时候,用户程序还在分配空间)
    • -XX:CMSInitiatingOccupancyFraction 设置触发GC
    • 如果内存预留不够,会引起concurrent mode failure
  • 使用串行收集器作为后备

  • CMS参数

    • -XX:+UseCMSCompactAtFullCollection Full GC 之后,进行一次整理,引起停顿
    • -XX:+CMSFullGCsBeforeCompaction 设置几次Full GC后,进行一次碎片整理
    • -XX:ParallelCMSThreads 设定CMS的线程数量

GC参数整理

-XX:+UseSerialGC                        //新生代,老年代串行收集器
-XX:SurvivorRatio                       //survivor:eden
-XX:NewRatio                            //新生代:老年代
-XX:+UseParNewGC                        //新生代并行收集
-XX:+UseParallelGC                      //新生代并行收集,吞吐量
-XX:+UseParallelOldGC                   //老年代并行收集
-XX:ParallelGCThreads                   //垃圾回收线程数
-XX:+UseConcMarkSweepGC                 //新生代并行,老年代CMS+串行收集
-XX:ParallelCMSThreads                  //CMS线程数
-XX:CMSInitiatingOccupancyFraction      //
-XX:+UseCMSCompactAtFullCollection      //垃圾回收后,是否进行一次内存压缩
-XX:CMSFullGCsBeforeCompaction          //多少次CMS垃圾回收,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled           //允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction  //永久区占用率达到这一百分比时,启动CMS回收
-XX:UseCMSInitiatingOccupancyOnly       //只有在到达阀值的时候,才进行CMS回收

性能监控工具

//系统工具,初步定位
uptime              //Linux命令
pidstat -p xxxx -u 1 3 -t       //-u显示CPU
pidstat -p xxx -d 1 3 -t        //-d显示io

//java自带工具
jps             //列出java进程
-l
-v
-m

jinfo           //

jmap                                //堆信息
-histo xxx > xxx.log
-dump:format=b,file=filepath xxx    //

jstack
-l              //打印锁信息
-m              //打印java和native的帧信息
-F              //强制dump
jstack xxx >> filepath

JConsole
jvisualvm

堆分析

MAT是基于eclipse软件

偏向锁

  • 大部分情况下是没有竞争的
  • 偏心,锁会偏向于当前已经占有锁的线程
  • 将对象头Mark的标记设置为偏向,并将线程ID写入对象头中
  • 只要没有竞争,获得偏向锁的线程,在将来进入同步块,不需要做同步
  • 当其他线程请求相同的锁时,偏向模式结束
  • XX:+UseBiasedLocking,默认启动
  • 在竞争激烈的场合,偏向锁会增加系统负担
  • 在jvm刚启动时,过几秒钟才会打开偏向锁。-XX:BiasedLockingStartupDelay=0刚启动即启用偏向锁

轻量级锁

  • 嵌入在线程栈中
  • 如果对象没有被锁定
    • 将对象头的Mark指针保存到锁对象中
    • 将对象头设置为指向锁的指针
  • 轻量级锁失败,存在竞争,升级为重量级锁
  • 竞争激烈,性能下降

自旋锁

  • 如果线程可以很快获得锁,那么可以不在OS层挂起线程,让线程做几个空操作(自旋)
  • JDK1.6 -XX:+UseSpinning 开启
  • JDK1.7 中去掉此参数,改为内置实现
  • 如果同步块很长,自旋失败,会降低系统性能
  • 如果同步块很短,。。。。

总结

  • 不是Java语言层面的锁优化方法
  • 内置于JVM中的获取锁的优化方法,和获取锁的步骤
    • 偏向锁可用,会先尝试偏向锁
    • 轻量级锁可用,尝试轻量级锁
    • 以上都失败,自旋锁
    • 最后普通锁

锁优化

  • 减小锁粒度
  • 锁分离(读锁、写锁、LinkedBlockingQueue等等)
  • 锁粗化,频繁进入锁
  • 锁消除,JVM自动消除
    • -server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
  • 无锁
    • CAS(Compare and Swap)
    • 非阻塞同步
    • java.util.concurrent.atomic.AtomicInteger

Class文件

B       //byte
C       //char
D       //double
F       //float
I       //int
J       //long
S       //short
Z       //boolean
V       //void
L       //对象
[       //数组
[[      //二维数组

字节码执行过程

  • 程序计数器
  • 局部变量表
  • 操作数栈
sipush xxx      //将xxx压入堆栈当中
istore_1        //操作数栈 -> 局部变量表1的位置
iload_1         //局部变量表位置1 -> 操作数栈
iadd            //结果入栈
idiv
ireturn         //返回操作数栈顶元素
_nop            //空指令
ldc             //常量池中项入栈
pop             //
dup             //复制栈顶一个字长,入栈

JIT just in time

  • 将一些热点代码编译成机器码
-XX:CompileThreshold=1000
-XX:+PrintCompilation

-Xint           //解释执行
-Xcomp          //全部编译执行
-Xmixed         //默认,混合

猜你喜欢

转载自blog.csdn.net/GodsLeft/article/details/57461146