`
C_J
  • 浏览: 124707 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java内存模型笔记

阅读更多

题记:
    看到C/C++写的内存池,不免了解下。同时学习下Java的Memory Model,学习和理解基于http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#jsr133

 

关于Java Memory Model

What is a memory model, anyway?

什么是内存模型?
In multiprocessor systems, processors generally have one or more layers of memory cache, which improves performance both by speeding access to data (because the data is closer to the processor) and reducing traffic on the shared memory bus (because many memory operations can be satisfied by local caches.)

At the processor level, a memory model defines necessary and sufficient conditions for knowing that writes to memory by other processors are visible to the current processor, and writes by the current processor are visible to other processors. Some processors exhibit a strong memory model, where all processors see exactly the same value for any given memory location at all times. Other processors exhibit a weaker memory model, where special instructions, called memory barriers, are required to flush or invalidate the local processor cache in order to see writes made by other processors or make writes by this processor visible to others. These memory barriers are usually performed when lock and unlock actions are taken; they are invisible to programmers in a high level language.

多核处理器通常都有1,2级或更多缓存,在处理器级别,内存模型通常规定了某处理器在写内存的时候需要对其他处理器都相互可见。一些处理器被设计成strong memory model,能让所有处理器在任何时候看到某内存里的值都是一样的。而有些处理器被设计成weawk memory model,拥有一个memory barrier的特殊结构,后面的没看明白。不管什么设计对于Programmer来说是透明的。

 

Recent trends in processor design have encouraged weaker memory models, because the relaxations they make for cache consistency allow for greater scalability across multiple processors and larger amounts of memory.

而“弱内存模型”在多处理器中,更能带来更好的可测试性和更多的内存。

 

The Java Memory Model describes what behaviors are legal in multithreaded code, and how threads may interact through memory. It describes the relationship between variables in a program and the low-level details of storing and retrieving them to and from memory or registers in a real computer system. It does this in a way that can be implemented correctly using a wide variety of hardware and a wide variety of compiler optimizations.

JAVA内存模型描述多线程操作内存的行为。描述了程序中的变量在内存中如何存储和获取的,或者在计算机中如何注册的。Java内存模型需要在各种硬件和编译器优化中正确实现这些东西。

 

Do other languages, like C++, have a memory model?

Most other programming languages, such as C and C++, were not designed with direct support for multithreading. The protections that these languages offer against the kinds of reorderings that take place in compilers and architectures are heavily dependent on the guarantees provided by the threading libraries used (such as pthreads), the compiler used, and the platform on which the code is run.

C/C++本身貌似不直接支持多线程,而是靠一些lib,比如pthreads。

The Java Memory Model was an ambitious undertaking; it was the first time that a programming language specification attempted to incorporate a memory model which could provide consistent semantics for concurrency across a variety of architectures.

 

What is meant by reordering?

There are a number of cases in which accesses to program variables (object instance fields, class static fields, and array elements) may appear to execute in a different order than was specified by the program. The compiler is free to take liberties with the ordering of instructions in the name of optimization. Processors may execute instructions out of order under certain circumstances. Data may be moved between registers, processor caches, and main memory in different order than specified by the program.For example, if a thread writes to field a and then to field b , and the value of b does not depend on the value of a , then the compiler is free to reorder these operations, and the cache is free to flush b to main memory before a .Most of the time, one thread doesn't care what the other is doing. But when it does, that's what synchronization is for.

大概意思是说代码的运行在程序里和寄存器、CPU缓存、主内存里是不一样的。这里举了个例子,比如一个写线程先写变量a,然后写变量b,a和b相互没有依赖的话,那么在缓存里,b可能会先于a写入主存中。大多数情况,单线程是不用关心这种顺序,但多线程的话,就需要用synchronize来保证顺序了。

 

What was wrong with the old memory model?

Nothing in the old memory model treated final fields differently from any other fiel.it was possible for a thread to see the default value of the field, and then at some later time see its constructed value.

在旧内存模型中,final变量和其他变量一样。比如String那种变量,这样会导致某一线程对于同一个final变量读取到不同的两个值。

 

 

What does synchronization do?

Synchronization ensures that memory writes by a thread before or during a synchronized block are made visible in a predictable manner to other threads which synchronize on the same monitor.

In the new memory model any memory operations which were visible to a thread before exiting a synchronized block are visible to any thread after it enters a synchronized block protected by the same monitor, since all the memory operations happen before the release, and the release happens before the acquire.

同步就是确保在写内存的时候只允许一个thread操作,而其他thread等待,当第一个thread写完从block出来后,下个线程才允许进入并查看修改后的内存,但需要第一个thread退出synchronization block后才可见修改后的值。而新模型中,第一个线程在block中发生写内存操作,其他threads就可以看到,而不需要等待当前thread退出block的时候。

 

How can final fields appear to change their values?

String s1 = "/usr/tmp";
String s2 = s1.substring(4); 
 

在老模型中,某线程是可以看到s2的offset刚开始为0,后来就变成4了。

 

How do final fields work under the new JMM?

The values for an object's final fields are set in its constructor. Assuming the object is constructed "correctly".In other words, do not place a reference to the object being constructed anywhere where another thread might be able to see it; do not assign it to a static field, do not register it as a listener with any other object, and so on. These tasks should be done after the constructor completes, not in the constructor.

能避免基本变量类型final型不出错的方法是:使用正确的构造函数,像下面这样

class FinalFieldExample {
  final int x;
  int y;
  static FinalFieldExample f;
  public FinalFieldExample() {
    x = 3;
    y = 4;
  }

  static void writer() {
    f = new FinalFieldExample();
  }

  static void reader() {
    if (f != null) {
      int i = f.x;
      int j = f.y;
    }
  }
}

bad construct

public FinalFieldExample() { // bad!
  x = 3;
  y = 4;
  // bad construction - allowing this to escape
  global.obj = this;
}

 

 

 

then threads that read the reference to this from global.obj are not guaranteed to see 3 for x

Now, having said all of this, if, after a thread constructs an immutable object (that is, an object that only contains final fields), you want to ensure that it is seen correctly by all of the other thread, you still typically need to use synchronization. There is no other way to ensure, for example, that the reference to the immutable object will be seen by the second thread. The guarantees the program gets from final fields should be carefully tempered with a deep and careful understanding of how concurrency is managed in your code.

上面举了2种构造方法的书写,说明了如何写对于final更安全的写法。甚至你还可以使用synchronization来保证final数据的安全。

 

What does volatile do?

     Volatile fields are special fields which are used for communicating state between threads. Each read of a volatile will see the last write to that volatile by any thread.They must also ensure that after they are written, they are flushed out of the cache to main memory, so they can immediately become visible to other threads. Similarly, before a volatile field is read, the cache must be invalidated so that the value in main memory, not the local processor cache, is the one seen.
     任何读取volatile的变量的只会从主内存里读取,而非缓存的值,任何写volatile变量后会立刻flush到主内存,这样保证所有的线程读的都是同一个值。

Here is a simple example of how volatile fields can be used:

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 42;
    v = true;
  }

  public void reader() {
    if (v == true) {
      //uses x - guaranteed to see 42.
    }
  }
}
 

 

Assume that one thread is calling writer , and another is calling reader . The write to v in writer releases the write to x to memory, and the read of v acquires that value from memory. Thus, if the reader sees the value true for v, it is also guaranteed to see the write to 42 that happened before it. This would not have been true under the old memory model.  If v were not volatile, then the compiler could reorder the writes in writer , and reader 's read of x might see 0.

这个例子说明了,如果v变量被写入主存了,那么x变量肯定为42,而这个在旧模型中是不成立的,是不保证顺序的。

 

Does the new memory model fix the "double-checked locking" problem?

In very early JVMs, synchronization was slow, and developers were eager to remove it -- perhaps too eager. The double-checked locking idiom looks like this:

为了尽量减少synchronization带来的开销,出现了double-check的方法

// double-checked-locking - don't do this!

private static Something instance = null;

public Something getInstance() {
  if (instance == null) {
    synchronized (this) {
      if (instance == null)
        instance = new Something();
    }
  }
  return instance;
}

 This looks awfully clever -- the synchronization is avoided on the common code path. There's only one problem with it -- it doesn't work. Why not? The most obvious reason is that the writes which initialize instance and the write to the instance field can be reordered by the compiler or the cache, which would have the effect of returning what appears to be a partially constructed Something. The result would be that we read an uninitialized object. There is no way to fix it using the old Java memory model. More in-depth information can be found at Double-checked locking: Clever, but broken and The "Double Checked Locking is broken" declaration

JSR133说这样做是愚蠢的,原因是初始化instance变量和执行getInstance()方法的顺序是能被编译器或缓存打乱的,在旧模型中,是没有办法避免这个问题。原因没看明白。

 

Many people assumed that the use of the volatile keyword would eliminate the problems that arise when trying to use the double-checked-locking pattern. In JVMs prior to 1.5, volatile would not ensure that it worked (your mileage may vary). Under the new memory model, making the instance field volatile will "fix" the problems with double-checked locking, because then there will be a happens-before relationship between the initialization of the Something by the constructing thread and the return of its value by the thread that reads it.

Instead, use the Initialization On Demand Holder idiom, which is thread-safe and a lot easier to understand:

很多人认为volatile可以解决这个问题,在旧模型中不行,在新模型中”可以“,没看明白。

最好的线程安全方式如下:

 

private static class LazySomethingHolder {
  public static Something something = new Something();
}

public static Something getInstance() {
  return LazySomethingHolder.something;
}
 

 

 

 

 

 

BTW:
    OS默认就进行内存管理,只是他的scale范围是整个OS,需要管理整个OS的内存情况,有自己的内存空闲表,但如果应用程序频繁申请和释放内存的话,使用OS自身的复杂的内存管理开销相对较大,因为OS的可用内存可能零散的分布在整个内存区域里而不连续,所以很多应用会自己来管理内存来提高效率,办法是从OS那边开出一个适当大小的内存出来,自己应用所有的new和delete都在这块区域上做,自己管理显然提高了代码的复杂度,也会产生很多Bug。

    JVM也是开出一大片内存,用-Xmx来指定,自己管理内存申请和释放。因为JVM内存存在3大区域,其中young generation用 -Xmn指定,n=new,做了个小试验:


在写这个的时候,想了一个很傻的问题,如果我把-Xmx=1024m -Xmn=1m,运行个很大的app,app会不会outmemory,后来想了下才知道自己很傻,显然不会,GC会启动,object入survivor区域,eden又有内存了。

 

写内存池貌似需要考虑很多问题,这是个非常容易出现crash的地方。

内存池大小

池不够,需要增加内存池

需要对象大小是否要split

是否需要给每个对象加head

内存池锁问题

共享内存

池中寻找空闲内存的算法

平台相关性

 

 

 

 

 

  • 大小: 12.7 KB
分享到:
评论
6 楼 C_J 2011-04-20  
独爱Java 写道
好像实际用处并不是很大,只是多了解了有这个东西而已。。。


其实上面有几个知识点

1个关于构造线程安全的构造函数的
2个关于volatile赋值顺序的问题
5 楼 独爱Java 2011-04-20  
好像实际用处并不是很大,只是多了解了有这个东西而已。。。
4 楼 C_J 2011-04-18  
ctoeye 写道
莫名其妙,搞什么鬼东西。这三个东西有什么不好明白的?用得着长篇大乱?
以前单例一般方法加synchronize,保证取实例时的线程安全。但性能有问题,于是出现volatile属性配合双得加锁double-check,用来提高性能。但是volatile在旧版本jdk中往往不生效,这是java本身的问题。在新版本的jdk中没有问题。


哦,我不懂,只是来学习下,表鸡冻。
3 楼 ctoeye 2011-04-18  
莫名其妙,搞什么鬼东西。这三个东西有什么不好明白的?用得着长篇大乱?
以前单例一般方法加synchronize,保证取实例时的线程安全。但性能有问题,于是出现volatile属性配合双得加锁double-check,用来提高性能。但是volatile在旧版本jdk中往往不生效,这是java本身的问题。在新版本的jdk中没有问题。
2 楼 C_J 2011-04-18  
1 楼 C_J 2011-04-18  
JE有个BUG,在博客里之前发布的文章,再发布到论坛竟然不认为是论坛的新帖,需要顶 一 下

相关推荐

    尚硅谷_宋红康_深度解析Java内存原型

    此文对于java初学者,想了解清楚java内存结构的童鞋,很有参考价值

    java面试笔试题库java学习笔记开发教程互联网公司面试资料大全合集.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    jvm内存模型个人笔记

    java初学者了解jvm相关知识增强

    java学习笔记java学习笔记

    什么是Java? 答:1) 编程语言(先编译再解释); 2) 开发环境; 3) 运行环境; 4) 部署环境; ...3. Java的特点 答:1) 简单(Java语法是C++...Java采用的指针模型可以消除重写内存和数据崩溃的可能) 7) 多线程(多线程编

    JVM历史发展和内存回收笔记.rar

    虚拟机的历史版本和JAVA内存分配,未来的虚拟机技术, 运行时数据区域,方法的出入栈,栈上分配

    JSR-133-Java Memory Model Specification 中文版+英文版+参考资料

    JSR133-memory_model-1_0-pfd2-spec.pdf JSR133中文版.pdf j-jtp02244-Java.theory.and.practice-Fixing.the.Java.Memory.Model.Part1/2.pdf Java Memory Model ...深入理解Java内存模型之系列篇 - CSDN博客.pdf

    免费分享 Java面试笔记 面试八股文 计算机网络基础

    Java并发编程:ThreadLocal、Java内存模型、锁、并发工具类、线程池等;JVM(Java虚拟机):Java内存管理详解、垃圾回收机制、垃圾回收器等;MySQL:基础知识、存储引擎、日志、SQL优化、数据索引、锁、事务、高可用...

    Java并发编程实战-读书笔记

    《Java并发编程实战》个人读书笔记,非常详细: 1 简介 2 线程安全性 3 对象的共享 4 对象的组合 5 基础构建模块 6 任务执行 7 取消与关闭 8 线程池的使用 9 图形用户界面应用程序 ...16 Java内存模型

    笔记-9、JMM和底层实现原理1

    目录 并发编程领域的关键问题 1 现代计算机物理上的内存模型 2 Java内存模型(JMM) 2 JVM对Java内存模型的实现 3 Java内存模型带来的问题

    java面试笔试资料java笔试题大集合及答案题库java笔试题汇总资料188个合集.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    java面试笔试题库java软件设计java笔试题大集合及答案文档资料合集300MB.zip

    Java内存模型的历史变迁.docx Java在游戏服务器开发中的应用.docx java基础总结大全.txt Java开发与技术挑战——关于技术的技术思考.docx Java框架研发思考.docx Java程序员们最常犯的10个错误.docx java程序员的...

    核心Java笔记(高清晰,自己亲自制作)

    Java采用的指针模型可以消除重写内存和数据崩溃的可能) 6) 多线程(多线程编程的简单性是Java成为流行的服务器端开发语言的主要原因之一) 7)安全(用Java可以构建防病毒和防篡改的系统) 9) 动态(Java可随意增加新的...

    java虚拟机知识点整理

    高效并发-java内存模型与线程 线程安全与锁优化 1 标记-清除算法:首先标记所有需要回收的对象(引用计数或可达性分析算法标记),在标记完成后统一回收所有被标记的对象。 缺点:效率问题,标记和清除两个过程效率都...

    01 - Java并发编程与高并发解决方案笔记-基础篇

    详细的讲述了并发、高并发、CPU Cache、CPU多级缓存、CPU多级缓存 - 缓存一致性(MESI)、CPU多级缓存-乱序执行优化、Java内存模型(Java Memory Model,JMM)、并发的优势和风险...等等图文并茂详解

    学习深入理解Java虚拟机的前几章笔记

    包括jvm 的内存模型 对象的创建过程 垃圾回收算法 垃圾回收器 内存分配和回收策略

    互联网Java面试训练营.rar

    12. Java面试- JVM 内存模型讲解 13. 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题 14. 2020年大厂Java面试前复习的正确姿势(800+面试题附答案解析) 15. 大白话聊聊Java...

    美团和蚂蚁金服面试笔记.pdf

    Java内存模型 线程公有方法区:Java方法区和堆一样,方法区是一块所有线程共享的内存区域,他保存系统的 类信息。比如类的字段、方法、常量池等。方法区的大小决定系统可以保存多少个类。如果 系统定义太多的类,...

    JVM学习笔记(一)

    一份JVM学习的笔记,含查看JVM运行时信息\JVM垃圾收集信息\JVM锁信息等

    java8集合源码分析-Notes:笔记

    多线程:多线程生成的原因(Java内存模型与i++操作解析)] [Java 多线程:生产者消费者问题] [Java 多线程:synchronized 关键字(修饰类,方法,静态方法,代码块)] [Java 多线程:Lock 接口(接口方法分析,...

Global site tag (gtag.js) - Google Analytics