jvm概念梳理

文章目录
  1. 1. 现代CPU下程序运行
    1. 1.1. 缓存一致性
    2. 1.2. 指令重排序
    3. 1.3. 逃逸分析
  2. 2. 逃逸分析优化
  3. 3. happen-before原则
  4. 4. GC垃圾回收
    1. 4.1. 定义垃圾
    2. 4.2. 内存中的分配
    3. 4.3. 清除方式

class文件 动态代理 静态代理

javac 编译 .java文件 为 class文件,后面.class文件转换为 机器指令

现代CPU下程序运行

多CPU –>CPU寄存器 –> 高速缓存cache –> 内存RAM

img

缓存一致性

多个线程可能不在一个CPU中运行,那么对于的变量可能不太一致,需要同步 volatile

指令重排序

对编译的时候对代码进行重排序,优化执行的效率,但是是一个变量,多个线程去执行的时候也行会导致,执行的变量的问题,不是说这个重排序是错的比如说不相干的变量查询,可能在CPU执行过程中可能有先后顺序的差异.

逃逸分析

相当于 你方法内的变量 需要传递出去,给别人用,不是基础类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static StringBuffer craeteStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb;
}

public static String createStringBuffer(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}

第一段代码中的sb就逃逸了,而第二段代码中的sb就没有逃逸。

使用逃逸分析,编译器可以对代码做如下优化:

一、同步省略。如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。

二、将堆分配转化为栈分配。如果一个对象在子程序中被分配,要使指向该对象的指针永远不会逃逸,对象可能是栈分配的候选,而不是堆分配。

三、分离对象或标量替换。有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在CPU寄存器中。

作者:HollisChuang
链接:https://juejin.cn/post/6844903639308304397
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

逃逸分析优化

针对上面第三点,当一个对象没有逃逸时,可以得到以下几个虚拟机的优化。

1) 锁消除

我们知道线程同步锁是非常牺牲性能的,当编译器确定当前对象只有当前线程使用,那么就会移除该对象的同步锁。

例如,StringBuffer 和 Vector 都是用 synchronized 修饰线程安全的,但大部分情况下,它们都只是在当前线程中用到,这样编译器就会优化移除掉这些锁操作。

锁消除的 JVM 参数如下:

  • 开启锁消除:-XX:+EliminateLocks
  • 关闭锁消除:-XX:-EliminateLocks

锁消除在 JDK8 中都是默认开启的,并且锁消除都要建立在逃逸分析的基础上。

2) 标量替换

首先要明白标量和聚合量,基础类型和对象的引用可以理解为标量,它们不能被进一步分解。而能被进一步分解的量就是聚合量,比如:对象。

对象是聚合量,它又可以被进一步分解成标量,将其成员变量分解为分散的变量,这就叫做标量替换。

这样,如果一个对象没有发生逃逸,那压根就不用创建它,只会在栈或者寄存器上创建它用到的成员标量,节省了内存空间,也提升了应用程序性能。

标量替换的 JVM 参数如下:

  • 开启标量替换:-XX:+EliminateAllocations
  • 关闭标量替换:-XX:-EliminateAllocations
  • 显示标量替换详情:-XX:+PrintEliminateAllocations

标量替换同样在 JDK8 中都是默认开启的,并且都要建立在逃逸分析的基础上。

3) 栈上分配

当对象没有发生逃逸时,该对象就可以通过标量替换分解成成员标量分配在栈内存中,和方法的生命周期一致,随着栈帧出栈时销毁,减少了 GC 压力,提高了应用程序性能。

happen-before原则

  • 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。

  • 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。

  • volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。

  • 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。

GC垃圾回收

定义垃圾

  1. 引用计数算法
  2. 可达性分析算法

内存中的分配

主要是手机heap(堆内存)主要是堆内存最大,同时是零碎的不像 stack(栈内存)是在方法调用完或者方法的引用完后就自动回收的.

目前堆内存中主要是初始化好的对象实例,和对象实例的类里面的变量,

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象

  • 方法区中类静态属性引用的对象

  • 方法区中常量引用的对象

  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象

Java 堆(Java Heap)是JVM所管理的内存

image-20221209125328758

清除方式

1.标记清除算法

2.复制算法

3.标记整理算法

参考链接: