08-Java对象的内存

1.java对象的布局---对象的组成

1.1 对象在内存中的组成

  • 对象头、实例数据、对齐填充(数据对齐)

注意:对齐填充是,java中规定对象要是8个字节的倍数,如果一个对象不是8个字节的倍数,就会进行对齐填充。 数据对齐的理由:比如64位的计算机一次可以处理64位也就是8个字节的数据,如果可以保证每个对象都是8个字节的倍数,可以提供效率

1.2 打印一个对象在内存中的组成形式

  • 可以添加jol(OpenJDK 开源的一个工具)的maven依赖,调用api来打印

2.Java类的加载过程

参考博客:https://www.cnblogs.com/wangsen/p/10838733.htmlarrow-up-right

1 加载 (loading)

  • 1.1 通过类的全限定名获取该类的二进制字节流。

  • 1.2 将二进制字节流所代表的静态结构转化为方法区的运行时数据结构。

  • 1.3 在内存中创建一个代表该类的 java.lang.Class 对象,注意类对象是放在堆中,

    作为方法区这个类的各种数据的访问入口 

怎样获取类的二进制字节流,JVM 没有限制。除了从编译好的 .class 文件中读取,还有以下几种方式:

从 zip 包中读取,如 jar、war 等
从网络中获取
通过动态代理生成代理类的二进制字节流
从数据库中读取

加载阶段与连接阶段的部分内容交叉进行,但这两个阶段的开始仍然保持先后顺序。

2 连接 (linking)

3 初始化 (Initialization)

4 使用 (Using)

5 销毁 (Unloading)

3.JMM (java memory model)

    1. lock (锁定)

    1. read(读取)

    1. load (载入)

    1. use (使用)

    1. assign (赋值)

    1. store (存储)

    1. write (写入)

    1. unlock (解锁)

4.可见性

  • 线程修改后的共享变量能够及时从工作内存刷新到主内存中

  • 其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中

5.Java 语言层面支持的可见性实现方式

  • synchronized

  • volatile

  • ReentrantLock

6.synchronized的功能

  • 原子性(同步)

  • 可见性

JMM 关于synchronized的两条规定:

  • 线程解锁前(线程退出synchronized代码块前),必须把共享变量的最新值刷新到主内存中

  • 线程加锁时,如果工作内存中共享变量的值被标记为失效,需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)

这样就实现了线程解锁前对共享变量的修改在下次加锁时对其他线程可见

7.指令重排序

    1. 编译器优化的重排序 (编译器优化)

    1. 指令级并行重排序(处理器优化),双核或多核

    1. 内存系统的重排序(处理器优化),内存系统读写缓存做的优化

特点:as-if-serial

  • 无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(Java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial语意)

  • 重排序不会给单线程带来内存可见性问题

  • 多线程中程序交错执行时,重排序可能会造成内存可见性问题

8. volatile 实现可见性

深入来说:通过加入内存屏障和禁止重排序优化来实现的

  • 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令

  • 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令

通俗的讲:volatile变量在线程内每次被访问时,都强迫从主内存中重读该变量的值, 而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存,这样任何时刻,不同的线程总能看到该变量的最新值

线程写volatile变量的过程:

  • 1.改变线程工作内存中volatile变量副本的值

  • 2.将改变后的副本的值从工作内存刷新到主内存

线程读volatile变量的过程

  • 1.从主内存中读取volatile变量的最新值到线程的工作内存中

  • 2.从工作内存中读取volatile变量的副本

9.volatile 不能保证变量复合操作的原子性

总结:volatile 保证可见性,不保证原子性

10.volatile 使用场景

要在多线程中安全的使用volatile变量,必须同时满足:

1.对变量的写入操作不依赖其当前值

  • 不满足:bumber++ 、 count = count*5;

2.该变量没有包含在具有其他变量的不变式中

  • low 和 up 都是volatile 修饰的值 ,那么low<up 就不满足

11.synchronized 和 volatile 比较

  • volatile 不需要加锁,比synchronized 更轻量级,不会阻塞线程

  • 从内存可见性角度讲,volatile读相当于加锁,volatile写相当于解锁

  • synchronized 技能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性

12.synchronized 和 reentrantLock 的区别

  • jdk1.6 之前,synchronized 是重量级锁,它会调用操作系统内核完成线程的同步,

  • jdk1.8 之后,synchronized 经过优化,提升了效率,和reentrantLock不相上下

注意:

reentrantLock实现的主要技术点:自旋、park--unpark、 CAS(compare and set) 原子操作

Last updated