JVM调优

正文

  • 程序计数器PC
  • java虚拟机栈
  • 本地方法栈
  • 方法区
  • 指令集
  • GC
  • 垃圾回收器
  • 调优
  • 程序,线程,进程,纤程(协程)
  • 常用参数
  • 垃圾回收算法

#java从编译到执行,x.java-》javac-》x.class-》java-》classloader-》Java类库-》字节码解释器-》执行引擎-》os硬件;cafe babe(magic),0000(minor version:.0)0034(major version:52),再接着0010constant_poll_count常量池16-1个常量,0号做了预留;0b二进制,0八进制,0x十六进制;long double加了volatile就是原子性;access flag》this_class>super class》interface count》interfaces》fteld_count》fields_count》fields》methods_count》method_info》attribute_count》attribute;加载过程:loading–>linking (verification,preparwetion,resolution)–》initalizing(会执行静态语句块);类加载器:Bootstrap(加载rt.jar等核心类,C++实现,一般为空值);Extension(加载扩展包,ext下的包);App(加载classpath指定内容);自定义类加载器;自定义类加载器;双亲委派机制,主要是为了安全。System.getProperties()//获取属性。System.lineSeparator()//换行。clz=loadClass;clz1=l.loadClass =clz1.混合模式:一段代码被多次执行很多次,会被放到本地,其他的则用解释器解释执行。bytecode interpretoer解释器。JIT-just in time compiler编译器。-Xmixed使用混合模式(默认),-Xint使用解释模式,Xcomp使用纯编译模式。自定义类加载器:继承classLoader,重写findClass。lazyloading五种情况:new,get static,put static,invoke static,访问final变量除外;检测热点代码:-XX:compileThreshold=10000(默认10000);一个calss被load进来,是通过define被创建出来放在堆里;
打破双亲委派:重写loadClass,threadContextClassLoader。热启动,热部署,扔到tomcat目录。
verification(校验是否合规),preparation(静态成员变量默认值),resolution(类,方法,属性等符号引用【二进制字节码】,例:java.lang.object解析为直接引用)。
intializing:设初始值,并执行静态语句块。DCL单例–加volatile:如果实现对对象进行初始化,再把地址给他,没有任何影响;指令重排情况下,如果是先给的地址,但是对象在赋默认值与初始化之间,会产生错误。
现代CPU数据一致性=缓存锁(MESI…)+锁总线。读取缓存以cacheline为基本单位,多数缓存为64字节(B),512位;long对应八个字节。1位=1比特;一字=2字节;一字节=8位;一字=16位。位于同一缓存行的两个不同数据,被两个不同的CPU锁定,会产生伪共享。缓存行对齐,long p1,p2,p3,p4,p5,p6,p8.乱序读执行:比如有的指令比较慢,执行一条的同时,会同时执行另一条;CPU有一个特别高速的缓存,合并写(只有四个字节)。volatile保证有序。CPU级别内存屏障:sfence,lfence,mfence;java intellock汇编指令有序性保障。
volatile实现细节:字节码层面ACC_VOLATILE.JVM层面:stroestorebarrier-写-storestorebarrier,loadloadbarrier-读-loadloadbarrier。OS和硬件层级:hsdis hotspot dis assembler;win使用lock指令实现;linux是屏障+指令+屏障+lock。
synchronized:字节码层面:方法(ACC_SYNCHRONIZED),代码块(monitor enter和monitor exit)。jvm层面:C和C++调用了操作系统提供的同步机制。os和硬件层级:lock。
对象:创建过程–load-》link-》init-》申请对象内存-》成员变量赋默认值-》调用构造方法(成员变量赋初始值,执行构造方法语句,例super)。
对象存储布局和字节数,观察虚拟机配置:java-XX:+printCommandLineFlags-version。普通对象–1,对象头 markword 8个字节;2 classpoint指针-XX:+usecompressedclasspointer为四个字节,不开启为八个字节;3实例数据,-XX:+usecompressdOops(ordinary object pointers)为四个字节,不开启位八个字节 4padding对齐,8的倍数。数组:多了个数组长度(4个字节)。
对象头存储了锁状态,对象的hashcode,线程id。分代年龄,锁标志位等信息。
GC年龄是15,就是因为对象头是四位。当一个对象计算过identifiedhashcode后,不能进去偏向锁状态。对象定位(没有优势HotSpot):句柄池(垃圾回收快,速度慢);直接指针(速度快,垃圾回收慢)。

程序计数器PC

存放指令位置。

java虚拟机栈

每个线程对应一个栈,每个方法对应一个栈帧。栈帧包含:局部变量表,操作数栈,动态链接,方法出口。非static方法需要一个对象this。dup会在栈里原来的指针上面新加个指针,同时指向堆里的一块区域,当执行invokespecial的时候,会弹出复制的那个指针执行构造方法,把对象赋初始值。字节码指令中假如没有上两个步骤则证明2占字节数太多。
java1.4之后增加了一个Direct Memory,jvm直接访问内核空间,NIO,提高效率,实现zore copy。

本地方法栈

运行c++方法

包括新生代+老年代

方法区

1.8之前用的是perm space(永久代)实现,大小启动的时候指定,不能变,FGC不会清理,1.8及之后用的是meta space,不设定就是最大物理内存,会触发FGC清理。如何确定1.7字符串常量位于perm还是堆,一直创建就行。1.7之前运行时常量池位于方法区,1.8之后再堆里。
i=i++的运算。

指令集

基于栈。基于寄存器,hotspot的局部变量表类似于寄存器。赋值store,压栈load。invoke:invokeStatic,调用静态方法。invokeVirtual:多态,final,非静态。ArrayList l=new ArrayList;l.add(“2”)。invokeSpecial,可以直接定位的,不需要多态的方法。invokeinterface(上面的例子)。invokedynamic,lamada,或者反射。

GC

怎么寻找垃圾:reference count(引用计数)。root searching(跟可达)。跟对象:线程栈变量,静态变量,常量池,JNI指针。
GC算法:标记清除(MARK SWEEP):两遍扫描,效率低,容易产生碎片,适合存活对象高的情况,不适合伊甸园区,适合老年代。标记整理/压缩(Mark Compat):效率更低,老年代,两遍扫描。复制算法(copy):空间浪费,对象移动,指针引用调整,扫描一次,效率高,适合伊甸园区,新生代。
ZGC之前所有的垃圾回收都是分代的,SD之前不分代。

垃圾回收器

说明:除了Epsilon,ZGC,shenandoah之外的都是逻辑分代;G1是逻辑分代,物理不分代。新对象刚创建会在栈上分配。新生代YGC,MinorGC;老年代fullgc,majorgc。新生代包含(伊甸园区和servior from 和servior to 比例8:1:1):老年代=1:2。参数查询:java-XX:+printFlagfinal-version |grep xxx。zing就一个参数。目前没有不产生STW的垃圾回收器。jdk诞生就伴随着serial,为了提高效率诞生了PS;为了配合CMS,诞生了PN;CMS是1.4版本后期引入的,是里程碑式的GC,开启了并发回收,目前没有任何一个jdk版本,默认都是CMS;jdk8默认值parallel,jdk9默认是G1.java -XX:+printflagdfinal-version |grep NEWratio查看年轻代老年代比例,额外newsize。分区概念:内存泄漏memory leak,内存溢出out of memory。lambda会导致方法区溢出,-xss。C++回收内存,析构函数;重写finalize,每个finalize耗时200ms。如果有一个系统,内存一直消耗不超过10%,但是观察GC日志,发现FGC总是频繁产生,会是什么引起的,system.gc()(这个比较low)。-XX:+disableexplictGC system.gc()不管用,FGC.
SERIAL:stop the world(STW),线程停止安全点(safe point),单线程。
serial old:STW,单线程。
parnew:stw,ps的变种。
CMS:初始标记(只标记根,stw,单线程),并发标记(非根),重新标记(stw,标记并发标记过程的垃圾,多线程),并发清理(浮动垃圾)。缺点:CMS不行了,serial会上场。folating garbage(并发清理过程产生的垃圾),concurrent mode failure,降低触发cms阈值-xx:cmsinitatingOccupancyfraction
原来是68%,1.8成了92%,可以降低改成70%,越低内存浪费越多,降低保持老年代有足够的空间,promotionfailed。碎片化。
算法:三色标记+incremental update(增量更新,关注引用的增加a->d,把黑色重新标记为灰色,下次重新扫描)
parallel old(pc):stw,默认环境回收期,多线程。
parallel old(po):同上。
G1(10MS):1.7,1.8稳定,任然有YGC(STW)FGC概念。算法:三色标记+satb(snaphot at the beginning,关注引用的删除,当b->d消失时,把这个引用推到gc的栈,保证d还能被gc扫描到)。garbage first。特点:并发标记,并发回收,压缩空闲时间,不回延长gc的暂停时间,预测gc暂停时间,适合不需要实现很高吞吐量的场景。
ZGC(1MS):算法:颜色指针(colored pointers)+写屏障。pk c++。至少11,jdk13.
shenandoah:算法:颜色指针(colored pointers)+读屏障。
epsilon:debug。
内存对应:serial几十M。ps上百M-几个G.cms20G。G1上百g 。zgc 4t-16t。
垃圾回收器参数:-xx:+useserialGC=serial new +serial old.-xx:+userparnewgc=parnew +serial old.-xx:+useconcurrent marksweepgc=par new +cms+serial old.-xx:+useparallel(old)gc=ps+po.-xx:+userg1gc=g1.
heapdump:eden space 563k,94% used[0x000000000ff980000,0x00000000ffeb29,0x000000000000ffff0000]后面的内存地址指的是起始地址没使用空间结束地址没整体空间结束地址。
对象内存分配:线程本地分配(TLAB),占用eden,默认1%,多线程不用争用eden就可以申请资源提高效率,小对象。-xx:+doescapeanalysis-xx:+eliminateallocations -xx:+usetlab 关闭逃逸分析,栈上分配。对象何时进入老年代-xx:maxtenuringthreshold,ps 15 ,CMS 6 g1 15.动态年龄。分配担保:YGC期间,servior空间不够了直接进去老年代。

调优

概念:吞吐量:用户代码时间/(用户代码+垃圾回收),例ps+po,响应时间:stw越短,相应越好。同步消除:如果对象没有出现线程逃逸,那该对象的读写就不会存在资源的竞争,不存在资源的竞争则可以消除对该对象的同步锁。
日志参数:1 -xloggc:/opt/xxx/log/xxx-XXX-gc-%t.log
-xx:+usergclogfilerotation.-xx:numberofgclogfile=5.-xxgclogfilesize=200M-xx:+printgcdetails.-xx:+printgcdatestampsps-xx:+printgccause 2或者每天产生一个日志文件。
调优步骤:top ,top -hp【pid】。jinfo【pid】进程号,虚拟机详细信息,一般不用。jmap -histo【pid】|head -20,1 heapdump,oom的时候会自动产生堆转储文件-xx:+heapdumponoutofmemoryerror -XX:heapdumppath=user/local/base。2 很多服务器备份,停掉这台服务器,对其他没影响。 3在线工具(cmddline arthas)4 jmap -dump:format=b,file=xxx.pid–线上系统jmap期间会对线程产生很大影响,甚至卡顿(用前三种)额外:jcmapgc,heap_dump d:\dump\heap.hprof。jstack纤程情况,waiting,blocking。jstat-gc【pid】500(每500毫秒打印一次),一般不用。jconsole。jvisualvm使用jmx,jmx对性能有影响。jprofiler(最好用)。非可视化工具cmdline arthas.使用mat/jhat/jvisualvm进行dump文件分析。jhat-j-mx512 xxx.dump,拉到最后找到对应连接可以使用oql查找定位问题对象。
参数:java-xx:+printCommandlineflags hellogc等
arthas。

程序,线程,进程,纤程(协程)

程序运行就是进程,进程可以有很多线程,线程可以有纤程。用户态叫做纤程,切换轻;内核态叫线程,切换重。纤程启动的数量比线程多。jdk14都没看到纤程影子。

常用参数

CMS G1

垃圾回收算法

说明:G1与PS相比吞吐量降低了10%-15%,但是响应时间200ms。软件架构核心思想:分而治之,分层。
G1基础概念:G1把内存分为每一块一块的区域(1m-32m),包含:surivor eden old humongos。card table(只有一个):由于做YGC时,需要扫描整个old区,效率非常低,所以jvm设计了cardtable,如果一个old区cardtable中有对象指向Y区,就将它设为dirty,下次扫描时,只需扫描dirty card在结构上,cardtable用bitmap来实现,Y区不用标记。collectionset:被回收的card装到一个表里。rememberset:每个region里面都有个表格,记录着其他region到本region的引用,详细到对象级别,占region的10%,由于rset的存在,每次给对象赋引用的时候就会做一些额外的操作,在rset中做额外的记录。这个叫做GC的写屏障。新老年代动态,不用指定,新生代一般为5-60%。G1对象分配不了,就会产生fullgc,扩内存,提高cpu性能,降低mixedgc发生的阈值,默认45%。mixedgc相当于一个cms,回收region不分年轻代还是老年代-xx:initatingheapoccupancy percent=xxx默认45%
YGC(eden空间不足,多线程并行执行)FGC(system.gc,old空间不足)。并行筛选回收,复制顺带压缩。
三色标记:白色:未被回收;黑色:自己的孩子都被标记;灰色:自己被标记,孩子没有被标记。漏标 (本来是live object,但是由于没有被遍历所以被回收),黑色指向了白色,灰色指向的白色的没了。remark阶段值处理漏标的。

你可能感兴趣的:(指针,jvm,java,jdk,多线程)