深入理解JVM笔记
来源:互联网 发布:国际数据调研公司 编辑:程序博客网 时间:2024/05/16 03:01
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显示CPUpidstat -p xxx -d 1 3 -t //-d显示io//java自带工具jps //列出java进程-l-v-mjinfo //jmap //堆信息-histo xxx > xxx.log-dump:format=b,file=filepath xxx //jstack-l //打印锁信息-m //打印java和native的帧信息-F //强制dumpjstack xxx >> filepathJConsolejvisualvm
堆分析
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 //byteC //charD //doubleF //floatI //intJ //longS //shortZ //booleanV //voidL //对象[ //数组[[ //二维数组
字节码执行过程
- 程序计数器
- 局部变量表
- 操作数栈
sipush xxx //将xxx压入堆栈当中istore_1 //操作数栈 -> 局部变量表1的位置iload_1 //局部变量表位置1 -> 操作数栈iadd //结果入栈idivireturn //返回操作数栈顶元素_nop //空指令ldc //常量池中项入栈pop //dup //复制栈顶一个字长,入栈
JIT just in time
- 将一些热点代码编译成机器码
-XX:CompileThreshold=1000-XX:+PrintCompilation-Xint //解释执行-Xcomp //全部编译执行-Xmixed //默认,混合
0 0
- 深入理解JVM笔记
- 《深入理解jvm》笔记---第一章
- 深入理解JVM笔记三
- 深入理解jvm笔记1
- 20150906 深入理解JVM之入门笔记
- 《深入理解JVM》学习笔记(1)
- 《深入理解jvm》笔记---第二章
- 《深入理解jvm》笔记---第六章
- 《深入理解jvm》笔记---第七章
- 《深入理解jvm》笔记---第八章
- JVM相关笔记(深入理解Java虚拟机)
- 《深入理解jvm》笔记---第三章
- Java与JVM深入理解笔记
- 深入理解JVM笔记-第12章
- 深入理解JVM笔记-第13章
- 深入理解JVM笔记之内存管理机制
- 深入理解JVM--我的学习笔记
- 深入理解JVM
- DataList实现编辑,删除,更新,取消,记录转存功能
- 《算法竞赛入门经典》习题——Chapter 1
- 本地机器ping不通虚拟机解决方案
- atoi()函数
- 【Dubbo入门】——基础知识篇
- 深入理解JVM笔记
- 使用卷积神经网络区分猫和狗.md
- codewar c++ 8分题(2))
- Driver:搭建linux驱动开发环境、内核驱动开发基础、导出符号、打印优先级
- Java 学习日志(一)
- R语言中的SMOTE算法的参数解释
- 字符串赋值方式
- FirstWeek .Exam for Java Fundamental
- php 中 遇见namespace 命名空间