1:JVM基础知识
什么是JVM
1. java虚拟机,跨语言的平台,实现java跨平台
2. 可以实现多种语言跨平台,只要该语言可以编译成.class文件
3. 解释执行.class文件
java是跨平台的语言,JVM是跨语言的平台
  

JVM运行流程

1. preparation 默认值=>初始值=>
2. 类加载器(jvm是按需动态加载,采用双亲委派机制,自底向上检查该类是否已经加载)
3. 为什么要使用双亲委派,为了安全
4. 自定义ClassLoader(customer Class Loader)=>classpath指定内容(App)=>加载扩展包(jre.lib.ext *.jar)=>加载rt.jar等核心包(BootStrap)
 

常见的JVM

    1. Hotspot oracle
2. Jrocket BEA被oracle收购
3. J9 IBM
4. VM Microsoft
5. TaobaoVM 阿里
6. LiQuidVM 直接针对硬件 oracle
7. zing azul最新垃圾回收的业界标杆
2:ClassFileFormat
数据类型:u1 u2 u4 u8和_info(表类型)
1. _info的来源是hotspot源码中的写法
查看16进制格式的ClassFile
1. sublime/notepad/
2. IDEA插件-BinEd
有很多可以观察ByteCode的方法
1. javap
javap -v E:\JVM\out\production\JVM\com\bytecode\T0100_ByteCode01.class
  

2. JBE-可以直接修改

    3. JClassLib-IDEA插件
4. classfile组成
ClassFIle{
  u4 magic;
  u2 miner_version;
  u2 major_version;
  u2 constant_pool_count;
  cp_info constant_pool[constant_pool_count - 1];
  u2
}
5. 二进制文件详解
类加载

加载过程
1.Loading
双亲委派:从自定义类加载器到classpath下的App到ext再到bootstrap一层一层去找,检查该类是否已经加载,有就返回,
返回也是从父类向子类一层一层查找
CustomerClassLoader<=>App<=>Extension<=>Bootstrap
2.Linking
Verification 验证文件是否符合JVM规定(前两位ca fe ba be)
Preparation 静态变量赋默认值,不是初始值
Resolution
将类、方法、属性等符号引用解析为直接引用
常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
3.Initializing
调用类初始化代码 <clinit>,给静态成员变量赋初始值

双亲委派机制:CustomerClassLoader<=>App<=>Extension<=>Bootstrap

   

代码验证

    

为什么使用双亲委派:为了安全,防止篡改jdk原有的类加载器

    

classLoader源码解析

    findInCache -> parent.loadClass -> findClass()

parent是如何指定的,打破双亲委派

    用super(parent)指定
双亲委派的打破
如何打破:重写loadClass()
何时打破过?
i. JDK1.2之前,自定义ClassLoader都必须重写loadClass()
ii. ThreadContextClassLoader可以实现基础类调用实现类代码,通过thread.setContextClassLoader指定
iii. 热启动,热部署
1) osgi tomcat 都有自己的模块指定classloader(可以加载同一类库的不同版本)
自定义类加载器
extends ClassLoader
overwrite findClass() -> defineClass(byte[] -> Class clazz)
加密
public class T007_MSBClassLoaderWithEncription extends ClassLoader {
public static int seed = 0B10110110;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
File f = new File("c:/test/", name.replace('.', '/').concat(".testclass"));
try {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b=fis.read()) !=0) {
baos.write(b ^ seed);
}
byte[] bytes = baos.toByteArray();
baos.close();
fis.close();//可以写的更加严谨
return defineClass(name, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name); //throws ClassNotFoundException
}
public static void main(String[] args) throws Exception {
encFile("com.koukay.jvm.hello");
ClassLoader l = new T007_MSBClassLoaderWithEncription();
Class clazz = l.loadClass("com.koukay.jvm.Hello");
Hello h = (Hello)clazz.newInstance();
h.m();
System.out.println(l.getClass().getClassLoader());
System.out.println(l.getParent());
}
private static void encFile(String name) throws Exception {
File f = new File("c:/test/", name.replace('.', '/').concat(".class"));
FileInputStream fis = new FileInputStream(f);
FileOutputStream fos = new FileOutputStream(new File("c:/test/", name.replaceAll(".", "/").concat(".testclass")));
int b = 0;
while((b = fis.read()) != -1) {
fos.write(b ^ seed);
}
fis.close();
fos.close();
}
}
代码执行方式
-Xmixed 混合执行 默认模式
-Xint 解释执行
-Xcomp 编译执行


JMM
硬件数据一致性
硬件层面:离CPU越近,效率越高,但是容量越小

     主内存同一缓存行的两个数可能同时被加载到L1和L2中,导致整个缓存行数据发生改变,解决方案-加总线锁,但是效率低


位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的伪共享问题
读取缓存以cache line为基本单位,目前64bytes
 

     解决方案-加总线锁,但是效率低

    
    效率高一点的解决方案,使用MESI缓存一致性协议(现代CPU的数据一致性实现 = 缓存锁(MESI ...) + 总线锁)
https://www.cnblogs.com/z00377750/p/9180644.html

     MESI协议中的状态

    https://www.cnblogs.com/z00377750/p/9180644.html
CPU中每个缓存行(caceh line)使用4种状态进行标记(使用额外的两位(bit)表示):
M: 被修改(Modified)
该缓存行只被缓存在该CPU的缓存中,并且是被修改过的(dirty),即与主存中的数据不一致,该缓存行中的内存需要在未来的某个时间点(允许其它CPU读取请主存中相应内存之前)写回(write back)主存。
当被写回主存之后,该缓存行的状态会变成独享(exclusive)状态。
E: 独享的(Exclusive)
该缓存行只被缓存在该CPU的缓存中,它是未被修改过的(clean),与主存中数据一致。该状态可以在任何时刻当有其它CPU读取该内存时变成共享状态(shared)。
同样地,当CPU修改该缓存行中内容时,该状态可以变成Modified状态。
S: 共享的(Shared)
该状态意味着该缓存行可能被多个CPU缓存,并且各个缓存中的数据与主存数据一致(clean),当有一个CPU修改该缓存行中,其它CPU中该缓存行可以被作废(变成无效状态(Invalid))。
I: 无效的(Invalid)
该缓存是无效的(可能有其它CPU修改了该缓存行)。
对齐缓存行,可以提高效率

      CPU为提升效率,会乱序执行

    

      乱序问题

    cpu为了提高指令执行效率,会在一条指令执行的过程中(比如去内存读取数据)去同时执行另一条指令,
前提是两条指令没有依赖关系
如果一个cpu在执行的时候需要访问的内存都不在cache中,cpu必须要通过内存总线到主存中取,那么在数据返回到cpu这段时间内(这段时间大致为cpu执行成百上千条指令的时间,至少两个数据量级)干什么呢? 答案是cpu会继续执行其他的符合条件的指令。比如cpu有一个指令序列 指令1 指令2 指令3 …, 在指令1时需要访问主存,在数据返回前cpu会继续后续的和指令1在逻辑关系上没有依赖的”独立指令”,cpu一般是依赖指令间的内存引用关系来判断的指令间的”独立关系”,具体细节可参见各cpu的文档。这也是导致cpu乱序执行指令的根源之一。
写操作合并
当cpu执行存储指令时,它会首先试图将数据写到离cpu最近的L1_cache, 如果此时cpu出现L1_cache未命中,则会访问下一级缓存。速度上L1_cache基本能和cpu持平,其他的均明显低于cpu,L2_cache的速度大约比cpu慢20-30倍,而且还存在L2_cache不命中的情况,又需要更多的周期去主存读取。其实在L1_cache未命中以后,cpu就会使用一个另外的缓冲区,叫做合并写存储缓冲区。这一技术称为合并写入技术。在请求L2_cache缓存行的所有权尚未完成时,cpu会把待写入的数据写入到合并写存储缓冲区,该缓冲区大小和一个cache line大小,一般都是64字节。这个缓冲区允许cpu在写入或者读取该缓冲区数据的同时继续执行其他指令,这就缓解了cpu写数据时cache miss时的性能影响
当后续的写操作需要修改相同的缓存行时,这些缓冲区变得非常有趣。在将后续的写操作提交到L2缓存之前,可以进行缓冲区写合并。 这些64字节的缓冲区维护了一个64位的字段,每更新一个字节就会设置对应的位,来表示将缓冲区交换到外部缓存时哪些数据是有效的。当然,如果程序读取已被写入到该缓冲区的某些数据,那么在读取缓存数据之前会先去读取本缓冲区的。
经过上述步骤后,缓冲区的数据还是会在某个延时的时刻更新到外部的缓存(L2_cache).如果我们能在缓冲区传输到缓存之前将其尽可能填满,这样的效果就会提高各级传输总线的效率,以提高程序性能。
https://www.cnblogs.com/liushaodong/p/4777308.html
如何保证特点情况下不乱序
硬件内存屏障 X86(CPU内存屏障)
sfence:store| 在sfence指令前的写操作必须在sfence指令后的写操作前完成
Ifence:load| 在Ifence指令前的读操作必须在Ifence指令后的读操作前完成
mfence:modify/mix | 在mfence指令前的读写操作必须在mfence指令后的读写操作前完成
Intel lock汇编指令
原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序
java内存模型中volatile变量在写操作之后会插入一个store屏障,在读操作之前会插入load屏障,一个类的final字段会在初始化之后插入一个store屏障,来确保final字段在构造函数初始化完成并可被使用时可见
JVM级别如何规范(JSR内存屏障)
LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,
在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,
在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,
在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,
在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。
volatile的实现细节
字节码层面
ACC_VOLATILE
JVM层面
volatile内存区的读写都加屏障
StoreStoreBarrier
volatile 写操作
StoreLoadBarrier
LoadLoadBarrier
volatile 读操作
LoadStoreBarrier
OS和硬件层面
hsdis - HotSpot Dis Assembler
windows lock 指令实现 | MESI实现
https://blog.csdn.net/qq_26222859/article/details/52235930
synchronized实现细节
字节码层面
ACC_SYNCHRONIZED
monitorenter monitorexit
JVM层面
C C++ 调用了操作系统提供的同步机制
OS和硬件层面
X86 : lock cmpxchg / xxx
https://blog.csdn.net/21aspnet/article/details/88571740
java并发内存模型
java线程之间通讯由java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见,从抽象的角度来看,
JMM定义了线程和主内存之间的关系,线程之间的共享变量存储在主内存中,每个线程都有个私有的本地内存,本地内存存储了共享变量副本,本地内存是JMM的抽象概念,并不真实存在
请解释一下对象的创建过程
将class load到内存
校验class规范,静态变量初始化默认值,解析指针引用
静态变量赋初始化值
为对象分配内存
成员变量赋默认值
调用构造方法
检分零头初(对象创建的几个步骤:检查加载 分配空间 设置零值 设置对象头 对象初始化)

观察虚拟机配置

    java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=333908032 -XX:MaxHeapSize=5342528512 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_231"
Java(TM) SE Runtime Environment (build 1.8.0_231-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.231-b11, mixed mode)
对象在内存中存储布局
普通对象
对象头:markword 8 对象状态,被回收多少次
ClassPointer指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节 指向哪个class
实例数据
引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 变量 Oops Ordinary Object Pointers
Padding对齐,8的倍数
数组对象
对象头:markword 8
ClassPointer指针
数组长度:4字节
数组数据
Padding对齐 8的倍数
对象头包括什么
对象头2位表示锁,gc标记分代年龄
gc年龄为什么为最大为15,因为他只有4位
new object()占用多少字节 16 对象头8,引用指针4,4个padding,不压缩还是16个字节
public class T03_SizeOfAnObject {
/**
* 压缩后
* 16
* 16
* 32
* 不压缩
* 16
* 24
* 40
* @param args
*/
public static void main(String[] args) {
//Padding对齐,8的倍数
//对象头8,指针引用(分压缩和不压缩,压缩是4,不压缩是8),如果是8不加padding,如果是4得加4padding,不压缩:16,压缩后16
System.out.println(ObjectSizeAgent.sizeOf(new Object()));
//对象头8,指针引用(分压缩和不压缩,压缩是4,不压缩是8),再加上4个数组长度,如果压缩就不加padding,如果不压缩需要加4padding,不压缩:24,压缩后16
System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));
//对象头8,指针引用(分压缩和不压缩,压缩是4,不压缩是8),id是4,name是4,age是4,b1是1,b2是1,o是4,b3是1,不压缩:40,压缩后32
System.out.println(ObjectSizeAgent.sizeOf(new P()));
} //一个Object占多少个字节
// -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
// -XX:-UseCompressedClassPointers表示不压缩
// Oops = ordinary object pointers
private static class P {
//8 _markword
//4 _class pointer
int id; //4
String name; //4
int age; //4 byte b1; //1
byte b2; //1 Object o; //4
byte b3; //1 }
}
对象定位
句柄池 (指针池)间接指针,节省内存
直接指针 访问速度快
就HotSpot而言,他使用的是直接指针访问方式进行对象访问,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。
https://blog.csdn.net/clover_lily/article/details/80095580
对象分配过程
先栈后堆,先伊甸后old
1.编译器通过逃逸分析判断对象是在栈上分配还是堆上分配,如果是堆上分配则进入下一步。(开启逃逸分析需要设置jvm参数)
2.如果tlab可以放下该对象则在tlab上分配,否则进入下一步。
3.重新申请一个tlab,再尝试存放该对象,如果放不下则进入下一步。
4.在eden区加锁,尝试在eden区存放,若存放不下则进入下一步。
5.执行一次Young GC。
6.Young GC后若eden区仍放不下该对象,则直接在老年代分配。


 
JVM Runtime Data Area and JVM Instruction Set
a class life cycle
class (load link initialize)--> JVM (run engin) -->run-time data area -->GC

Runtime Data Area 运行时数据区


    Program Counter:(程序计数器)存放指令位置

    虚拟机的运行类似这样的循环
while(not end){
取pc中的位置,知道对应位置的指令;
执行该指令;
pc++;
} JVM Stack 虚拟机栈
每个JVM线程有个JVM栈,和线程同时创建
JVM栈存储的栈帧Frame
Frame - 每个方法对应一个栈帧,用于存储数据和部分结果,以及执行动态链接,方法返回值和抛出异常
Local Variable Table 本地变量表
Operand Stack 对于long的处理(store and load),多数虚拟机的实现都是原子的 jls 17.7,没必要加volatile
Dynamic Linking
动态链接是一个将符号引用解析为直接引用的过程
https://blog.csdn.net/qq_41813060/article/details/88379473 jvms 2.6.3
return address a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方 Heap 堆
Java 中的堆是 JVM 所管理的最大的一块内存空间,主要用于存放各种类的实例对象。线程间共享
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
Method Area 方法区
保存在着被加载过的每一个类的信息;
这些信息由类加载器在加载类的时候,从类的源文件中抽取出来;
static变量信息也保存在方法区中;
Perm Space (<1.8) 字符串常量位于PermSpace FGC不会清理 大小启动的时候指定,不能变
Meta Space (>=1.8) 字符串常量位于堆 会触发FGC清理 不设定的话,最大就是物理内存
Runtime Constant Pool 常量池
Native Method Stack 本地方法栈
Direct Memory 直接内存
JVM可以直接访问的内核空间的内存 (OS 管理的内存)
NIO , 提高效率,实现zero copy

指令集分类

    基于寄存器的指令集
基于栈的指令集
Hotspot中的Local Variable Table = JVM中的寄存器
常用指令:
store: istore_<n> Store int into local variable 出栈
load: iload_<n> Load int from local variable 压栈
pop: pop Pop the top operand stack value 弹栈
mul: imul Multiply int 乘法
sub: isub Subtract int 减法
invoke:
InvokeStatic: invokestatic Invoke a class (static) method 引用静态方法
InvokeVirtual: invokevirtual Invoke instance method; dispatch based on class 调用实例方法 基于类的分派
InvokeInterface: invokeinterface Invoke interface method 调用接口方法
InovkeSpecial: invokespecial Invoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes 调用实例方法,直接调用当前类及其父类的初始化方法
可以直接定位,不需要多态的方法 private 方法 , 构造方法
InvokeDynamic: invokedynamic Invoke a dynamically-computed call site 调用动态计算方法
JVM最难的指令 lambda表达式或者反射或者其他动态语言scala kotlin,或者CGLib ASM,动态产生的class,会用到的指令
案例解析
public class TestIPulsPlus {
public static void main(String[] args) {
int i = 8;
i = ++i;
System.out.println(i);
}
}

        public class TestIPulsPlus {

public static void main(String[] args) {
int i = 8;
i = i++;
System.out.println(i);
}
}

  

Garbage Collector and GC tuning
什么是垃圾
没有任何引用指向的一个对象或多个对象(循环引用)都是垃圾
如何定位垃圾
引用计数(ReferenceCount)
不能解决RC a->b->c->a 但是没有引用指向他们,所以他们是一团垃圾
跟可达算法(Root Searching) main开始的栈帧 静态变量.常量池 jni指针
GC Algorithm 常见垃圾回收算法
Mark-Sweep 标记清除 - 位置不连续 产生碎片 效率偏低(两遍扫描)
        拷贝算法 (copying) - 没有碎片,浪费空间
   
        标记压缩(mark compact) - 没有碎片,效率偏低(两遍扫描,指针需要调整)

    JVM内存分代模型(用于垃圾回收算法)

        部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
G1是逻辑分代,物理不分代
除此之外不仅逻辑分代,而且物理分代
新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace
永久代 元数据 - Class
永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)
字符串常量 1.7 - 永久代,1.8 - 堆
MethodArea逻辑概念 - 永久代、元数据
新生代 = Eden + 2个suvivor区
YGC回收之后,大多数的对象会被回收,活着的进入s0
再次YGC,活着的对象eden + s0 -> s1
再次YGC,eden + s1 -> s0
年龄足够 -> 老年代 (15 CMS 6)
s区装不下 -> 老年代
老年代
顽固分子
老年代满了FGC Full GC
GC Tuning (Generation)
尽量减少FGC
MinorGC = YGC
MajorGC = FGC
new:old 1:2
eden:s1:s2 8:1:1

        对象分配过程图

      

        栈上分配 

            private的小对象
只在方法内使用,没有对象指向
简单类型表示对象
无需调整
TLAB分配 Tlab Thread Local Allocation Buffer
占用eden
小对象
无需调整
测试栈上分配及线程本地分配效率:
-XX:-DoEscapeAnalysis-XX:-EliminateAllocations-XX:-UseTLAB
逃逸分析 标量替换 线程专有对象分配 对象什么时候进入老年代
指定YoungGC次数 XX:MaxTenuringThreshod
Eden+s1->s2动态年龄超过 s2的50% 把年龄最大的放入Old 常见的垃圾回收器


JDK诞生 Serial追随 提高效率,诞生了PS,为了配合CMS,诞生了PN,CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前没有任何一个JDK版本默认是CMS 并发垃圾回收是因为无法忍受STW
Serial 年轻代
STW
单线程的,串行回收
内存小
safe point 在安全点暂停
PS 年轻代 并行回收
STW
多线程
ParNew 年轻代
STW
配合CMS的并行回收
SerialOld 老年代
STW
单线程的,串行回收
ParallelOld 老年代
STW
多线程
ConcurrentMarkSweep CMS老年代 并发的, 垃圾回收和应用程序同时运行,降低STW的时间(200ms) CMS问题比较多,所以现在没有一个版本默认是CMS,只能手工指定 CMS既然是MarkSweep,就一定会有碎片化的问题,碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld 进行老年代回收 想象一下: PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW) 几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC
算法:三色标记 + Incremental Update CMS的问题
i. Memory Fragmentation 内存碎片
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction 默认为0 指的是经过多少次FGC才进行压缩
ii. Floating Garbage 浮动垃圾
Concurrent Mode Failure 产生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped
解决方案:降低触发CMS的阈值
PromotionFailed
解决方案类似,保持老年代有足够的空间
–XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让CMS保持老年代足够的空间
优化环境
有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G 的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G 的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了
为什么原网站慢?
很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW长,响应时间变慢
为什么会更卡顿?
内存越大,FGC时间越长
咋办?
PS -> PN + CMS 或者 G1
系统CPU经常100%,如何调优?(面试高频) CPU100%那么一定有线程在占用系统资源,
找出哪个进程cpu高(top)
该进程中的哪个线程cpu高(top -Hp)
导出该线程的堆栈 (jstack)
查找哪个方法(栈帧)消耗时间 (jstack)
工作线程占比高 | 垃圾回收线程占比高
系统内存飙高,如何查找问题?(面试高频)
导出堆内存 (jmap)
分析 (jhat jvisualvm mat jprofiler ... )
如何监控JVM
jstat jvisualvm jprofiler arthas top...
G1(10ms)
算法:三色标记 + SATB
ZGC (1ms) PK C++
算法:ColoredPointers + LoadBarrier
Shenandoah
算法:ColoredPointers + WriteBarrier
Eplison
PS 和 PN区别的延伸阅读:
https://docs.oracle.com/en/java/javase/13/gctuning/ergonomics.html#GUID-3D0BB91E-9BFF-4EBB-B523-14493A860E73
垃圾收集器跟内存大小的关系
Serial 几十兆
PS 上百兆 - 几个G
CMS - 20G
G1 - 上百G
ZGC - 4T - 16T(JDK13)
1.8默认的垃圾回收:PS + ParallelOld
常见垃圾回收器组合参数设定:(1.8)
-XX:+UseSerialGC = Serial New (DefNew) + Serial Old
小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
-XX:+UseParNewGC = ParNew + SerialOld
这个组合已经很少用(在某些版本中已经废弃)
https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future
-XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
-XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
-XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
-XX:+UseG1GC = G1
Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC
java +XX:+PrintCommandLineFlags -version
通过GC的日志来分辨
Linux下1.8版本默认的垃圾回收器到底是什么?
1.8.0_181 默认(看不出来)Copy MarkCompact
       1.8.0_222 默认 PS + PO

JVM学习笔记-从底层了解程序运行(一)的更多相关文章

  1. JVM学习笔记-从底层了解程序运行(二)

    解决JVM运行中的问题 一个案例理解常用工具 测试代码: /** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */ public class T15_FullGC_Problem01 ...

  2. 孙鑫MFC学习笔记3:MFC程序运行过程

    1.MFC中WinMain函数的位置在APPMODUL.cpp APPMODUL.cpp中是_tWinMain,其实_tWinMain是一个宏#define _tWinMain WinMain 2.全 ...

  3. 孙鑫MFC学习笔记1.Windows应用程序运行机理

    1.MSG结构 hwnd:窗口句柄 message:消息类型 wParam & lParam:消息的附加信息(比如键值) time:消息被投递的时间 tip:typedef的作用是从变量类型区 ...

  4. C++学习笔记1(Windows程序运行原理及程序编写流程)

    窗口产生过程,句柄原理,消息队列,回调函数,窗口关闭与应用程序退出的工作关系,使用VC++的若干小技巧,stdcall与Lessonecl调用规范的比较,初学者常犯错误及注意事项.以下是应用程序与操作 ...

  5. JVM学习笔记——内存结构篇

    JVM学习笔记--内存结构篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存结构部分 我们会分为以下几部分进行介绍: JVM整体介绍 程序计数器 虚拟机栈 本地方法栈 堆 方法 ...

  6. JVM学习笔记——垃圾回收篇

    JVM学习笔记--垃圾回收篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的垃圾回收部分 我们会分为以下几部分进行介绍: 判断垃圾回收对象 垃圾回收算法 分代垃圾回收 垃圾回收器 ...

  7. JVM学习笔记——类加载和字节码技术篇

    JVM学习笔记--类加载和字节码技术篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的类加载和字节码技术部分 我们会分为以下几部分进行介绍: 类文件结构 字节码指令 编译期处理 类 ...

  8. JVM学习笔记——内存模型篇

    JVM学习笔记--内存模型篇 在本系列内容中我们会对JVM做一个系统的学习,本片将会介绍JVM的内存模型部分 我们会分为以下几部分进行介绍: 内存模型 乐观锁与悲观锁 synchronized优化 内 ...

  9. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

随机推荐

  1. Django-初见

    目录 安装&启动 HTTP请求URL路由 项目APP 返回 页面内容 给浏览器 路由 路由子表 创建数据库 定义数据库表 创建数据库表 Django Admin 读取数据库数据 过滤条件 对资 ...

  2. oracle三个重要参数文件:pfile和spfile和init.ora

    Oracle中的参数文件是一个包含一系列参数以及参数对应值的操作系统文件.它们是在数据库实例启动第一个阶段时候加载的, 决定了数据库的物理 结构.内存.数据库的限制及系统大量的默认值.数据库的各种物理 ...

  3. ui-route的一般用法 ,比较简单。在平时项目也用到,但是我也要记下来,虽然是'借鉴的',取其精华

    这是个啥? ui-router是一个web客户端的路由解决方案.我觉得它最大的作用是将web界面的设计分块了. 分块分层 最初的web访问模型,是这样的: 我们访问page1,然后访问page2... ...

  4. shell语法习题练习进阶版

    第4章 shell语法深度习题练习 4.1 使用if,case,函数的方法将服务改成system(centos6) 4.1.1 if方法 4.1.1.1 system实现 4.1.1.1.1 编写代码 ...

  5. python基础练习题(题目 三数排序。)

    day40 --------------------------------------------------------------- 实例066:三数排序 题目 输入3个数a,b,c,按大小顺序 ...

  6. Docker安装 Ubuntu Centos

    Ubuntu 安装Dokcer 1. 删除旧版本Docker安装包和依赖项 sudo apt-get remove docker docker-engine docker.io containerd ...

  7. Python学习笔记: 用pprint更漂亮的打印数据

    pprint是一个标准库,它提供了pprint()函数 ,用来打印复杂数据时更漂亮 >>> from pprint import pprint >>> data = ...

  8. xpath & csv文件读写

    原理:拿到网页源代码并且进行分析 关键词:etree     .xpath      a[@href="dapao"]      a/@href       text() impo ...

  9. Red Hat牵头推进NVFS文件系统

    开源Linux 长按二维码加关注~ 上一篇:Linux中几个正则表达式的用法 由 Red Hat 工程师牵头的团队,正在为 Linux/开源社区研究名为 NVFS 的文件系统.NVFS 的目标是成为像 ...

  10. 用漫画了解Linux内核到底长啥样

    一个执着于技术的公众号 原文链接:http://985.so/hRL6 往期精彩 ◆  干货 | 给小白的Nginx10分钟入门指南 ◆  什么是集群?看完这篇你就知道啦! ◆  干货 | Linux ...