最近又鼓捣jvm,然后结合着VisualVM监控、jvm的书,对jvm又有一些理解体会,今天主要聊聊jvm的线程模型,内存模型,以及跟开发比较相关的GC。
线程模型,jvm的线程就对应os的线程,据说linux的kernel3.x之后,也出现了OS内核线程支持,之前实现是轻量级进程,比如你线程搞多了,或者kernel内存留少了,unable to create new native thread。
内存模型,类似多核cpu的高速缓存,跟主内存的关系,jvm是线程栈与主内存,于是只要线程共享的栈内变量,就得加volatile,那个内存屏障、#lock信号量就不说了,你在jdk的Concurrent常见volatile的。
来张图:
内存管理,算是最常见的方面,首先分代收集,即新生代、老年代、永久代,你可以设置分代的大小比例,当然G1是region划分的;
然后收集算法,1. 新生代标记复制,eden\s0\s1,默认8:1:1的比例,关于晋升老年代,也可以设置对象年龄,总之减少FullGC的次数;2. 老年代标记整理,SerialOld、ParOld、ParallelOld都是这个算法,但是CMS收集器是标记清除;3. 分区收集,分为不同region独立回收,就是garbageFrist的G1收集器。
最后收集器,一般多核cpu应用并发的收集器,关于stop-world问题,可以设置收集时间,收集比例等进行控制;当然CMS、G1收集器会把并发标记、并发清理跟初始标记、重新标记分开,响应时间上会比其他的收集器要好。
来张图:
对于垃圾收集器的参数配置,其实还是跟算法紧密相关的,现在cpu都多核,一般都选用并行收集器,图上除了Serial的;
xms、xmx:一般设为一样大,扩充堆大小也得有不小开销,当然一般系统都会预热;
当然,OS运行也是要内存的,别把OS的内存挤掉了.
xmn:产生的对象朝生夕灭就多设置点,否则就反过来嘛;
survivorratio:晋升老年代有个担保分配,为减少FullGC,根据应用监控调节;
metaspacesize:jdk1.8把常量池啥的都迁移出去了,存储类加载器&类信息;
parallelGCThreads:并行收集线程数,如果cpu富裕就加点呗;
maxTenuringThreshold:对象晋升老年代的年龄,如果朝生夕灭就多设置点;
pretenureSizeThreshold:大对象晋升老年代,详情请Google;
useAdaptiveSizePolicy:动态调整晋升年龄及大小,这个参数好啊;
当然各个收集器有自己的参数,比如maxGCPauseMillis、CMSInitiatingOccupancyFraction等。
至于字节码引擎执行、字节码的提前编译,表示还没咋接触。