jvm虚拟机总结

11,383 阅读6分钟

我正在参加「掘金·启航计划」

一 常用命令

1. jps 查看进程号

2. jinfo -flags 进程号 
    运行时参数
3. jmap
    查看堆信息
    

二 Jvm体系结构

通过classLoader  将 class文件加载到运行时 数据区,然后通过执行引擎执行

1. 方法区 是规范(永久代是实现,原空间替代永久代)
    类信息(包名、类名、接口信息、方法信息/静态变量、运行时常量池)
    1.6 的时候常量池放在永久代[方法去]1.7 的时候常量池放在了堆中
    1.8 移除了永久代的概念,变成了metaSpace
2. 堆:存放实例数据
    2.1 新生代
        Survivor 0
        Survivor 1
        Eden
    2.2 老年代
3. Java栈
    基本类型、对象引用/实例方法
    线程私有,每个方法都有一个栈帧,栈帧存放的:局部变量表、操作数栈、动态链接、方法出口[上一个方法执行的地方]
4. 本地方法栈
5. 程序计数器
    线程私有,指向下一条指令的地址
    

三 堆+栈+方法区的交互关系

栈中的对象引用 指向堆中的对象(实例数据、对象类型指针),堆中的对象类型指针指向方法区(Class 类型)中的对象类型数据

四 回收算法 和 GC收集器

1. 引用计数法[循环引用的问题]
2. 可达性分析[hotspot使用 root 节点]
    2.1 作为GcRoot的对象:
        虚拟机栈(局部变量表)的本地变量、静态变量
        方法区的类属性
        方法区的常量
        本地方法栈的变量
3. 回收算法
    3.1 标记清除
        耗时、造成碎片空间
    3.2 复制算法  
        复制耗时、浪费空间
        存活对象较多的场合,只在新生代使用
    3.3 标记压缩
        耗时,存活对象较多的场合,适合老年代
4. 收集器
    Serial: 单线程,简单高效、新生代复制算法
    parNew:多线程
    Parallel Scavenge: 新生代复制算法、吞吐量优先、自适应调节
    Serial Old:老年代-标记整理算法
    CMS:标记清除算法、并发收集、低停顿
    G1:并发和并行、分代收集、标记整理算法、将堆分为多个大小相等的区[region]
    
5. 方法去的回收
    5.1 回收废弃常量
    5.2 没有被引用的常量
    5.3 无用类
        5.3.1 该类所有实例被回收
        5.3.2 加载该类的classloader已被回收
        5.3.3 class对象没有被引用

五 对象分配策略

1. 优先分配到 新生代 的 eden区
2. 老年代
    提升:新生代的对象达到一定年龄
    大对象直接分配到老年代
    分配担保:eden 复制到 Survivor 区的时候,空间不足的情况下
    动态分配:从最小年龄开始累加,对象综合大于 Survivor 50% 的时候,大于当前age的对象会分配到老年代

六 类加载器

1.启动类加载器,Bootstrap Classloader,加载JAVA_HOME\lib等底层类
2.扩展类加载器,Extension ClassLoader,加载\lib\ext,扩展类的加载
3.应用程序类加载器,Application ClassLoader,加载ClassPath中的类库
4.自定义类加载器,通过继承ClassLoader实现,加载我们自定义类的双亲委派模型

双亲委派模型:类加载时,先将请求委派给父类加载器加载完成,最底层、顶层Bootstrap ClassLoader加载器,如果父类无法加载,子类尝试自己加载。

双亲委派的好处:避免同一个类被多次加载,每个加载器只能加载自己范围的类。

5.类加载的生命周期:
类加载分三个步骤:加载、链接、初始化

流程为:
加载-》验证-》准备-》解析-》初始化 -》使用-》卸载

七 引用类型

1.强引用
  强引用的对象不会被回收
2.软引用
  只有在内存不足时被回收
3.弱引用
  一定会被回收,生命周期就是下次垃圾回收之前
4.虚引用目的就是被回收的时候收到系统通止规,对其生存时间没有影响,决定于其他引用它是否被回收

八 对象的创建和结构

1. 步骤:
    1.1 是否能在常量池中找到类符号引用,并检查是否被加载、解析、初始化,如果没有就进行类加载
    1.2 分配内存
        1.2.1 内存规整:指针碰撞
        1.2.2 内存不规整:空闲列表
        1.2.3 线程安全:本地线程分配TLAB,每个线程一个空间,互不干涉
2. 对象内存布局[结构]
    2.1. 对象头[8个字节]
        2.1.1 运行时数据区:哈希值、GC分代年龄、线程持有锁状态、偏向线程id
        2.1.2 类型指针:确定是哪个类的实例
    2.2. 实例数据 [4个字节]
        代码中定义的各种类型字段的内容
        相同宽度的数据放在一起
    2.3. Padding(对齐填充)
        4个字节的整数倍[占位符]

九 调优案例和实战

1. 问题
    1.1 高性能硬件上的策略
        问题:大内存使得GC停顿时间变长
        控制Full GC 频率关键:大多数对象生命周期不应太长
    1.2 集群间同步导致对象溢出
    1.3 堆外内存溢出
        1.3.1 Direct Memory
        1.3.2 线程堆栈
        1.3.3 socket缓冲区
        1.3.4 虚拟机和GC
    1.4 内部命令导致系统缓慢
        调用外部命令,虚拟机会克隆一个和当前虚拟机一样的进程
    1.5 服务端jvm 奔溃
        异步调用另一个服务器上的服务,响应缓慢,导致请求积压
    1.6 不恰当的数据结构导致内存占用过大
    
2 调优
    2.1 类加载时间
    2.2 编译
    2.3 GC时间
        2.3.1 扩大新生代 防止YGC
        2.3.2 扩大老年代 防止FGC
 
 
 java -XX:+PrintFlagsFinal -version 
 

十 反射的三种方法

 class.forname    .class    getClass

十一 什么是Java内存模型:

这里概念一定要明确,内存模型不是那个运行时数据图。

java内存模型全称为Java Memory Model,是java虚拟机为了java程序能够正常运行而制定的一套规范,规范中规定了JVM中的数据如何与RAM的数据进行交互。

java中分为主内存和工作内存,主内存中存放共享变量:实例字段、静态字段、数组对象元素。线程对共享数据的操作必须在工作内存中进行。这就是为什么需要java内存模型,其实就是对内存的管理,线程间的变量值的传递都需要通过主内存完成,不能相互之间访问变量。