一个Java内存可见性问题的分析

如果熟悉Java并发编程的话,应该知道在多线程共享变量的情况下,存在内存可见性问题:

在一个线程中对某个变量进行赋值,在另外一个线程中读取该变量的值,读取到的可能仍然是以前的值;

这里并非说的是时序的问题,例如读取操作在赋值操作之前执行了,而是说,

即使在另外一个线程中循环读取该变量的值,也可能永远看不到变量的最新值,或者一段时间内看不到。

请看下面的代码片

 1 public class Main extends Thread {
 2     private static boolean flag = false;
 3
 4     @Override
 5     public void run() {
 6         while (!flag);
 7     }
 8
 9     public static void main(String[] args) {
10         Main m = new Main();
11         m.start();
12         try {
13             Thread.sleep(200);
14         } catch (InterruptedException e) {
15             e.printStackTrace();
16         }
17         flag = true;
18         try {
19             m.join();
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         System.out.println("done");
24     }
25 }

这段代码在Windows,Linux,MacOS下的HotSpot中运行都不能结束

将变量flag声明为volatile的话,程序可正常结束,

类似的程序在Android的dalvik下运行并没有问题

奇怪的是,如果增加一个print语句,它也可以正常结束。

如果查看字节码的话,发现除了flag变量的volatile标记之外,字节码并无不同

从这里我们可以推断,并非是字节码执行的问题,因此怀疑是JIT的缘故,关掉JIT,再次运行,果然就都正常了。

如果是JIT的话,生成了机器代码,按照CPU的缓存一致性协议,变量有可能短时间之内不可见,那也应该过一段时间就能可见了,计算机上同时运行着这么多程序,像store buffer这样的结构应该会不时被刷新到混存中,甚至内存中,应该不至于导致永远不可见。

那么到底是什么导致了永久不可见?不是字节码执行引擎,不是硬件问题,唯一可能的就是JIT生成的代码导致的了。

那么接下来就是查看JIT代码了:

时间: 2024-11-08 00:12:40

一个Java内存可见性问题的分析的相关文章

Java内存模型JMM简单分析

参考博文:http://blog.csdn.net/suifeng3051/article/details/52611310 http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html    http://www.cnblogs.com/dolphin0520/p/3613043.html 一.Java内存区域的划分 由于Java程序是交给JVM执行的,所以我们在谈Java内存区域分析的时候事实上是指JVM内存区域划分. 根

一个java内存泄漏的排查案例

这是个比较典型的java内存使用问题,定位过程也比较直接,但对新人还是有点参考价值的,所以就纪录了一下. 下面介绍一下在不了解系统代码的情况下,如何一步步分析和定位到具体代码的排查过程 (以便新人参考和自己回顾) 初步的现象 业务系统消费MQ中消息速度变慢,积压了200多万条消息,通过jstat观察到业务系统fullgc比较频繁,到最后干脆OOM了: ? 进一步分析 既然知道了内存使用存在问题,那么就要知道是哪些对象占用了大量内存. 很多人都会想到把堆dump下来再用MAT等工具进行分析,但du

CognitiveJ一个Java的人脸图像识别开源分析库

CognitiveJ 是一个开源的,支持 Java 8 API 的库,用于管理和编排 Java 应用和微软的Cognitive(Project Oxford)机器学习和图像处理库的项目,可以让你查询以及分析图像.一:人脸识别    1.人脸检测– 捕获脸部.性别.年龄等相关脸部特征以及图像的标志    2.表情检测 – 根据图像中的脸部信息推断出表情状态    3.验证 – 验证同一个人的两张不同表情的差异    4.识别 – 根据已知的人里识别出某个人    5.查找相似 -- 对人脸检测.分

[转]Java内存对象的逃逸分析

逃逸分析英文作Escape Analysis.在计算机语言编译器优化原理中,逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联. 当变量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他过程或者线程所引用,这种现象称作指针(或者引用)的逃逸(Escape). 在Java 并发编程书里有个例子程序清单3-7 谈到 this escape. 开始没有想明白, 仔细琢磨了些时间发现代码主要的问题是在建构函数中创建了一个匿名类,然后发布了这个匿名类

使用MAT分析Java内存

Overview MAT(Memory Analyzer Tool) 是一个JAVA Heaper分析器,可以用来分析内存泄露和减少内存消耗.分析Process showmap中的/dev/ashmem/dalvik-heap(deleted)一项所占用的Memory.可以参考我写的使用showmap分析系统内存占用情况一文. 下面就将一下如何使用Eclipse MAT分析Android应用程序内存的消耗.所需要的是已经安装ADT和SDK的Eclipse.然后可以在http://www.ecli

JDK 源码阅读 —— Java 内存模型

零. 为什么需要 Java 内存模型 为了让程序员忽略掉各种硬件和操作系统的内存访问差异, 也既无需关心不同架构上内存模型的差异, Java 在代码和硬件内存模型间又提供了一个 Java 内存模型. 一. 并发模型的分类 在并发编程中,需要处理两个关键问题:线程之间如何通信(线程之间以何种机制来交换信息, 有两种方式:共享内存和消息传递)及线程之间如何同步. 在共享内存的并发模型里(如 Java),线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态(主存)来隐式(对程序员透明)进行通

转: 【Java并发编程】之十四:图文讲述同步的另一个重要功能:内存可见性

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17288243 加锁(synchronized同步)的功能不仅仅局限于互斥行为,同时还存在另外一个重要的方面:内存可见性.我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且还希望确保当一个线程修改了对象状态后,其他线程能够看到该变化.而线程的同步恰恰也能够实现这一点. 内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果.为了确保所有的线程都能看到

【Java并发编程】之十四:图文讲述同步的另一个重要功能:内存可见性

加锁(synchronized同步)的功能不仅仅局限于互斥行为,同时还存在另外一个重要的方面:内存可见性.我们不仅希望防止某个线程正在使用对象状态而另一个线程在同时修改该状态,而且还希望确保当一个线程修改了对象状态后,其他线程能够看到该变化.而线程的同步恰恰也能够实现这一点. 内置锁可以用于确保某个线程以一种可预测的方式来查看另一个线程的执行结果.为了确保所有的线程都能看到共享变量的最新值,可以在所有执行读操作或写操作的线程上加上同一把锁.下图示例了同步的可见性保证. 当线程A执行某个同步代码块

Java多线程之内存可见性

1.什么是JAVA 内存模型 Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互. 具体说来, JVM中存在一个主存区(Main Memory或Java Heap Memory),对于所有线程进行共享,而每个线程又有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问,变量在程序中的传递,是依赖主存来完成的. Jav