深入理解Java的堆内存和线程内存
我们都知道Java对象都是在堆中创建的(开启逃逸分析的情况除外),比如一个线程中有一段这样的代码:
public class A{
public int xxx;
}
通过A a = new A();会在堆中创建一个对象,并引用a 指向了堆中对象的内存地址,也就是主内存中。
也就是说线程中的引用指向了主内存中的对象地址,很多Java程序员甚至以为因为持有引用,所以对这个引用的赋值或者读取都是直接根据地址操作主内存的对象,其实并不是这样的。
如果按照这个逻辑,线程中操作的对象就是主内存中对象(为了好理解,我直接认为主内存就是堆了);直接操作堆中对象, 那就是只有一个堆中对象,也不会存在多线程下不一致问题了,因为大家都是通过地址操作同一个对象,只有一个版本,就不会有不一致问题。
可事实并不是如此。线程内存和主内存是不一样的。当线程要读取a.xxx的时候,其实是通过该引用持有的内存地址去堆中读取这个对象的属性值,赋值给线程中的变量a.xxx;修改也一样,修改完了后将这个值覆盖堆中a的xxx属性的值(怎么实现稍后讲);
Java操作内存相关的指令有8个,lock(锁定),unlock(解锁),read(读取),load(载入),use(使用),assign(赋值),store(存储)
线程对象的操作主要是通过这个几个指令实现的,而不是我们想象的直接操作。
所以多线程下的时候,每个线程都去堆中读取对象的值,拷贝到自己线程变量中使用,修改完了再覆盖回去。这才会出现不一致和读到被别人修改的数据。
volatile关键字的作用之一就是每次使用前都和主内存(堆)进行上面的读取或者写回操作,保证了线程的可见性。如果按照线程对象就是直接操作堆中对象,那就根本就不需要这个关键字了,简单想象就知道线程中的对象也不是堆中的对象这个事实了,使用这个关键字就是希望线程中的对象和堆中的对象是一致的。
回答刚才留下的坑,那我们到底是怎么去堆中读取对象的内容的呢,比如上面的a.xxx, 对象的属性的开始地址相对对象开始地址是有一个偏移量的,
每个类型都有其规定的长度,只要从开始地址读取这个类型长度的地址就可以了。下面演示一种牛B的修改内存地址处值的方法。
这个偏移量的获取方法如下:
Field field = a.getClass().getDeclaredField("xxx");
long sexOffset = unsafe.objectFieldOffset(field); //sexOffset 就是这个偏移量;
unsafe 是sun.misc.Unsafe类,是不能通过new出来的。可以通过反射获取,Unsafe类是无锁机制。
public native Object getObjectVolatile(Object arg0, long arg1);
这个方法的实现类中就可以通过对象引用和偏移地址来获取其属性值。
这个类中还有很多牛B方法,很多都是通过传说的CAS算法实现的的,其实这种算法没那么复杂就是比较和替换,比较堆中的该地址处的值是否和期望值一样,一样就执 行相应的修改操作,并返回真,不一样就放弃操作,返回假。
深入理解Java的堆内存和线程内存的更多相关文章
- 深入理解Java类加载器(二):线程上下文类加载器
摘要: 博文<深入理解Java类加载器(一):Java类加载原理解析>提到的类加载器的双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器的实现方式.在Java ...
- MySQL中内存分为全局内存和线程内存
首先我们来看一个公式,MySQL中内存分为全局内存和线程内存两大部分(其实并不全部,只是影响比较大的 部分): 复制代码 代码如下: per_thread_buffers=(read_buffer_s ...
- 深入理解Java虚拟机:垃圾收集器与内存分配策略
目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...
- 深入理解java:1.3.1 JVM内存区域的划分(运行时数据区)
学习Java GC机制,可以帮助我们在日常工作中 排查各种内存溢出或泄露问题,解决性能瓶颈,达到更高的并发量,写出更高效的程序. 我们将从4个方面学习Java GC机制, 1,内存是如何分配的: 2, ...
- 《深入理解Java虚拟机》学习笔记之内存分配
JVM在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域,如下图: 大致可以分为两类:线程私有区域和线程共享区域. 线程私有区域 程序计数器(Program Counter Regi ...
- 深入理解java虚拟机(一)虚拟机内存划分
Java虚拟机在执行Java程序时,会把它管理的内存划分为若干个不同的数据区.这些区域有不同的特性,起不同的作用.它们有各自的创建时间,销毁时间.有的区域随着进程的启动而创建,随着进程结束而销毁,有的 ...
- 深入理解Java虚拟机笔记——垃圾收集器与内存分配策略
目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...
- 深入理解java虚拟机(7)---线程安全 & 锁优化
关于线程安全的话题,足可以使用一本书来讲解这些东西.<Java Concurrency in Practice> 就是讲解这些的,在这里 主要还是分析JVM中关于线程安全这块的内容. 1. ...
- 深入理解java虚拟机-第13章-线程安全与锁优化
第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...
随机推荐
- Chainsql使用相关笔记
安装 按照github上chainsql的安装过程,这里主要介绍区块网络搭建过程(4个验证节点,其中只有一个节点[10.11.6.120]配置了数据库): 1.根据官方文档上配置文件的案例,修改cha ...
- 只知道ajax?你已经out了
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由前端林子发表于云+社区专栏 随着前端技术的发展,请求服务器数据的方法早已不局限于ajax.jQuery的ajax方法.各种js库已如雨 ...
- Java设计模式学习记录-GoF设计模式概述
前言 最近要开始学习设计模式了,以前是偶尔会看看设计模式的书或是在网上翻到了某种设计模式,就顺便看看,也没有仔细的学习过.前段时间看完了JVM的知识,然后就想着JVM那么费劲的东西都看完了,说明自己学 ...
- [JSOI2010] 连通数
Description Input 输入数据第一行是图顶点的数量,一个正整数N. 接下来N行,每行N个字符.第i行第j列的1表示顶点i到j有边,0则表示无边. Output 输出一行一个整数,表示该图 ...
- Mybatis之基于XML的调用存储过程与手动回滚事务
一.调用存储过程 一.返回单个值 1.存储过程准备 这里先创建一个存储过程,传入参数为age,传出参数为count.然后先测试一下是否正确. CREATE DEFINER=`root`@`localh ...
- 浩顺考勤机二次开发(第二版,附实测可用的demo)
1.背景 之前写过一次浩顺考勤机的二次开发,不过那个版本还是有一些问题,后来更换了新的考勤机,又拿到了新的二次开发包,所以就有了这次这个版本 2.关于考勤机的一些说明 2.1 首先要给考勤机设定ip, ...
- Node Redis 入门
基础准备:Node.Js .npm或cnpm.redis安装 1.建立一个项目文件夹,这里命名 wxfc ,打开命令行输入 npm install redis . 因为没有创建package.json ...
- 如何将一个HTML页面嵌套在另一个页面中
一 在原页面嵌入其他页面 1.使用iframe框架 客户端页面嵌套可以使用iframe的方法,弊端是必须事先想好被嵌套的页面在首页中要占多大的位置. 如果被嵌套页面太大,超过事先定义的宽度或高度,则首 ...
- JAVA中的集合容器操作类
目录 JAVA中的集合容器操作类 List集合 ArrayList的操作方法说明 LinkedList Stack Set Map Queue 总结 JAVA中的集合容器操作类 Java容器类库总共分 ...
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...