JVM常用工具,内存结构,垃圾收集,锁学习笔记(一)
JVM(JDK1.7)
查看JVM进程&参数
JPS
列出在主机上执行的虚拟机,语法jps [ options ] [ hostid ]
jps-m -->输出传递给JVM主方法的参数
jps -v -->输出传递给JVM的参数
通常以-X -D -XX等开始的标识符
JINFO
查看JVM进程的配置信息(包括未显示指定的配置参数).
1.打印所有配置信息
jinfo 24917
2.打印通过命令行参数传递给JVM的配置
jinfo -flags 24917
3.打印Java系统属性列表
jinfo -sysprops 24917
查看JVM运行时信息
JSTAT
查看JVM运行时的统计信息,包括类加载信息\编译信息\GC信息\
1.查看类加载信息
jstat -class -t 1958 1 1
2.查看Java堆的垃圾收集统计信息
jstat -gc -t 1958 1000 3
S0C | 幸存区0容量KB[下同] |
S1C | 幸存区1容量 |
S0U | 幸存区0使用量 |
S1U | 幸存区1使用量 |
EC | 新生代容量 |
EU | 新生代使用 |
OC | 老年代容量 |
OU | 老年代使用 |
PC | 持久带容量 |
PU | 持久带使用 |
YGC | 年轻带GC事件次数 |
YGCT | 年轻带GC时间 |
FGC | 全GC事件次数 |
FGCT | 全GC时间 |
GCT | 用于垃圾回收的总时间 |
3.查看实时的垃圾收集统计摘要
jstat -gcutil 24917 1000 5
S0 | 幸存区0使用比例 |
S1 | 幸存区1使用比例 |
E | 新生代使用百分比 |
O | 老年代使用百分比 |
P | 持久代使用百分比 |
YGC | 年轻代GC事件数量 |
YGCT | 年轻带GC时间 |
FGCT | 全GC事件次数 |
FGCT | 全GC时间 |
查看Java堆
JMAP
打印Java对内存映射.
1.打印对象共享内存映射(内存地址\共享内存大小\共享对象文件的绝对路径)
jmap 24917
2.将Java堆转储为hprof二进制格式到文件,可以使用JHAT工具来查看.
jmap -dump:format=b,file=./tomcat.hprof 24917
3. 打印Java 堆的概要信息,GC算法,堆配置等
jmap -heap 24917
MaxHeapSize =16848519168 (16068.0MB) -->最大堆大小
NewSize =1310720 (1.25MB) -->新生代大小
MaxNewSize =17592186044415 MB -->最大新生代大小
OldSize =5439488 (5.1875MB) -->老年代大小
NewRatio =2 -->新生代与老年代比例
SurvivorRatio =8 -->两个幸存区与新生代的比例
PermSize =21757952 (20.75MB) -->永久代大小
MaxPermSize =85983232 (82.0MB) -->最大永久代大小
G1HeapRegionSize = 0 (0.0MB)
查看Java栈
JSTACK
跟踪Java栈信息,为给定的进程或Core文件或者远程调试服务打印线程堆栈.对于每个Java帧,输出完整类名\方法名\字节代码索引\行号等信息.-m参数打印程序计数器,
1.打印Java堆栈信息,64位的JVM中
jstack -J-d64 24917
2.打印Java\Native的混合堆栈信息
jstack -m -J-d64 24917 |more
JVM参数
通常情况下启动一个Java应用程序就会启动JVM的虚拟机,虚拟机在启动时可以通过java指令传递参数给JVM.
java
用来启动一个java程序,两种格式:
1.java [ options ] class [arguments ]
需要指定main方法所在的类名,
2.java [ options ] -jar file.jar[ arguments ]
需要指定main所在的jar,且jar中需要使用清单文件申明main方法
Java运行时依次从以下类路径查找Class
1.bootstrap class path
java平台核心class,由JVM的 bootstrap类加载器加载,路径为{java_home}/jre/lib
2.installed extensions
基于Java扩展机制,扩展的Java核心模块,一般放在{Java_home}/jre/lib/ext
3.user class path
用户类路径,开发人员提供或其它第三方java程序包,通过-classpath 、-cp或者设置CLASS_PATH环境变量来引用,JVM通过放置在{java_home}/lib/tools.jar的程序来寻找和调用用户级别Class.优先级如下:
1.缺省,调用java的当前路径(.),class所在的当前目录.
2.CLASSPATH环境变量指定的路径
3.java指令的-classpath或者-cp的值
4.-jar指定的文件
标准参数
1.-classpath ,-cp
指定类路径,可以使用通配符;
2.-Dproperty=value
设置系统属性,可在程序汇总使用System.getproperts(“”)来获取;
3.verbose
显示冗余信息
-verbose:class à显示关于每一个类加载器的信息
-verbose:gc à报告每一个GC事件信息
4.-version
打印版本信息
非标准参数
1.-X à显示关于非标准选项的信息
2. -Xbootclasspath:bootclasspath
指定搜索启动类的目录、jar文件、压缩文件.基于此可以指定内置的JRE的路径
3.-Xbootclasspath/a:path
将指定目录中搜索的类添加到启动类路径中
4.-Xbootclasspath/p:path
5.-Xnoclassgc à禁用类垃圾收集
6.-Xloggc:file à输出GC报告到文件
7.-Xmnsize || -XX:NewSize à设置年轻代大小
8.-Xmsn à初始内存大小
9.-Xmxn à最大内存大小
可选的运行时选项
10.-XX:MaxGCPauseMillis=n à最大GC暂停时间
11.-XX:NewSize à年轻代大小
12.-XX:ParallelGCThreads=n à并行的垃圾收集器中线程数
13.-XX:+PrintGCDetails -XX:+PrintGCTimeStamps à打印垃圾收集信息和时间戳
14.-XX:+UseConcMarkSweepGC or-XX:+UseG1GC à使用CMS||G1收集器
15.-XX:+UseParallelOldGC à使用ParallOld 收集器
垃圾收集算法
标记清除
分标记、清除两阶段,首先标记需要回收的对象,标记完成后统一进行回收.
劣势:
1.标记、清除两个阶段的效率都不高;
2.空间回收问题,标记清除之后会产生大量不连续的内存区域碎片;
*****
对于效率问题后期的垃圾收集器都并行&并发为手段来降低在标记、清除阶段的效率问题,如ParNew、Par Scavenge、ParOld的并行的标记和清除;
如果使用了标记清除算法,需要处理内存碎片问题,CMS收集器支持在内存不足时执行内存碎片整理和执行若干次的不压缩的FGC后执行一次压缩的FGC来优化内存.
复制算法
为解决效率问题,内存划分为两块,每次将存活对象复制到另外一块内存,每次对整个半区的内存进行清理,目前的商业虚拟机大都采用这个算法来进行新生代的垃圾回收.
劣势:
1.将有效内存降低了一半,代价高;
2.不适合对象存活率较高的场景
3.如果不浪费50%的内存则需要内存担保
4.老年代一般不能直接使用这种算法,没有内存空间为其担保
**
一般的用法是将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor.回收时将Eden和Survivor中还存活的对象一次性的复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间,HostSpot虚拟机默认的Eden和Survior的大小比例是8:1,只有百分之10的内存会被浪费;Survior内存空间不足时有老年代进行内存分配担保.
标记整理
基于复制算法的缺点,且不适合老年代使用,标记整理算法先标记,后将存活的对象向一边移动,之后清理掉边界以外的内存区域.
分代收集
当前的商业虚拟机都采用分代收集的方法,根据对象存活周期的不同将内存划分为不同的块.Java中堆一般分为新生代和老年代。
1.新生代朝生夕死适合使用复制算法,
2.老年代对象存活时间长,没有额外的内存分配担保,一般使用标记-清理||标记整理算法
垃圾收集器
HostSpotJVM现有7中垃圾收集器,如下:
Serial&Serial Old
新生代采用复制算法,老年代采用标记整理算法
1.单线程,垃圾收集时停止用户线程”stop the world!”
2.Client模式下默认的新生代收集器
3.使用-XX:+UseSerialGC 显示的指定使用串行的垃圾收集器
ParNew&ParOld
新生代采用复制算法,老年代采用标记整理算法;为Serial收集器的多线程版本;
1.使用多个线程并行的处理垃圾回收工作,暂停所有用户线程;
2.-XX:+UseParallelOldGC 指定老年代使用Parallel Old垃圾收集器
3.-XX:+UseParNewGC 强制使用 ParNew 垃圾收集器
4.-XX:ParallelGCThreads 指定垃圾收集器的线程数量,默认等于CPU数量
5.只有ParalNew能与CMS配合工作;
Parallel Scavenge
于吞吐量密切相关,成为吞吐量收集器,与ParalNew相似,算法都一致;
1.Parallel Scavenge的目标是达到一个可控制的吞吐量,减少垃圾收集的时间,让用户代码获得更长的运行时间.
2.-XX:MaxGCPauseMillis à设置最大垃圾收集停顿时间;
3.-XX:GCTimeRatio à垃圾收集时间占总时间的比率;
4.-XX:+UseAdptiveSizePolicy à根据当前系统运行情况收集性能监控参数,动态调整各分代中的内存大小和相关参数.
CMS
CMS是老年代的垃圾收集器,使用标记清理算法,是一个并发的低停顿的收集器,
1.老年代收集器,并发清理容易带来内存碎片问题,以获得最短的停顿时间为目标,
2.并发收集,低停顿,适合于注重响应素的的场景,
3.-XX:+UseConcMarkSweepGC 指定使用CMS收集器
初始标记(stop the word) à 并发标记(与用户线程并发执行)à重新标记(并行标记)à并发清除à
1. 对CPU资源非常敏感,默认的收集线程数量为(cpu数+3)/4
2. 无法处理浮动垃圾,可能会出现失败,并发清除过程中用户线程产生的新垃圾,
3. 标记-清除算法产生的内存碎片,无法找到连续内存时提前触发一次Full GC,可使用-XX:+UseCMSCompactAtFullCollection 指定开启内存碎片的合并整理过程,该过程无法并发,默认开启但是不执行.
4. -XX:+CMSFullGCsBeforeCompaction配置执行多少次不压缩的Full GC后执行一次压缩清理.默认为0
5. 与Parallel 相比,CMS减少了执行老年代垃圾收集时应用的暂停时间,降低了吞吐量且需要占用更大的堆空间.
G1
Garbage-First
1. 并行&并发,并行清理,与用户线程并发执行;
2. 分代收集,收集范围包括新生代和老年代,堆内存布局有很大差别,将整个堆划分为多个大小相等的独立区域(Region),
3. 结合多种垃圾收集算法,空间整合,不产生碎片,整体看基于标记整理算法,局部看属于复制算法,
4. 可预测的停顿时间,可以明确指定M时间内垃圾收集消耗的时间不超过N时间
5. 面向服务端,针对有大内存\多处理器的机器,低GC延迟,
6. -XX:+UseG1GC 配置使用G1收集器,-XX:G1HeapRegionSize 设置每个Region的大小
初始标记(stop the word)à并发标记à最终标记(stop the world)à帅选回收
Java内存区域
线程私有
1. 程序计数器 -->JVM规范中唯一没有规定内存溢出的区域.
2. 虚拟机栈 -->超出栈深度报栈溢出异常,请求内存不足报内存溢出.
Java栈是方法执行的内存模型,Java栈中存储一个个帧栈,每个帧栈对应一个被调用的方法,帧栈结构包括(局部变量表\操作数栈\指向当前方法所属类的运行时常量池的引用\方法返回地址等);一个方法的执行伴随一个帧栈的创建\压栈和出栈操作.
每个线程都会有自己的帧栈互不干扰;
3. 本地方法栈 -->为执行本地方法服务;
共享区域
1. Java堆 -->用来存储对象&数组的区域
被所有线程共享,垃圾收集的主要区域;
2. 方法区 -->存储每个类的静态信息
所有线程共享,存储了每个类的信息(类名\方法信息\字段信息)、静态变量、常量以及编译器编译后的代码等.
方法区中包含运行时常量,每一个类或接口的常量池的运行时表示形势
Java加锁机制
Java中的线程安全
1. 不可变
2. 绝对线程安全,通常所说的线程安全的容器并不是绝对线程安全的
3. 相对线程安全,一般的同步容器为相对线程安全的
4. 线程兼容,对象本身非线程安全,可以正确的使用同步手段使其可以正确的并发使用
5. 线程对立,无论是否采用正确的同步都无法并发的使用
线程安全的实现方法
1. 互斥同步,互斥是因,同步是果,常见的Synchronized、ReentrankLock(可重入锁) ;进行线程阻塞和唤醒带来的性能问题à悲观并发控制.
2. 非阻塞同步,先操作在判断是否有冲突,常见的Atomic*类型的实现,通常情况下需要操作系统指令的支持(测试并设置\获取并增加\交换\比较并交换等语意),非阻塞同步的许多实现不需要将线程挂起所以成为非阻塞同步à乐观并发控制
3. 无同步方案,不涉及数据共享,可重入代码\线程本地存储
锁优化
1. 自旋锁&自适应自旋锁
2. 锁消除
3. 锁粗化
4. 轻量级锁
5. 偏向锁
使用-XX:+UseBiasedLocking使用偏向锁
https://www.cnblogs.com/f-zhao/p/6189104.html?utm_source=itdadao&utm_medium=referral
锁获取&锁释放
1. 无锁状态是最佳的 à不需要获取锁
2. 首先获取偏向锁 à出现竞争时升级为轻量级锁,同时释放锁
3. 轻量级锁,CAS加锁 à两个以上线程竞争锁,升级为重量级锁
4. 重量级锁,线程被阻塞,直到锁释放唤醒线程.
JVM(jdk1.8)
Java
差别还是挺大的,指令的名称有变化,记起来还是比较麻烦的,大概知道有哪些,用的时候man java查以下.
常见的要知道:
1. 如何为java程序指定不同的类路径
2. 如何调整JVM的运行时内存参数
3. 如何查看JVM运行时的各种统计状态,核心的如GC状态\GC日志\线程状态
4. 如何配置并选择合适的GC收集器
5. Java内存模型和锁的关系
6. Java中锁的种类,以及不同类型的实现[并发编程],锁优化