`
C_J
  • 浏览: 128297 次
  • 性别: 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笔记

    笔记会涉及类加载机制、内存模型、垃圾回收等JVM核心概念。 9. **设计模式**:笔记会介绍常见的23种设计模式,如单例模式、工厂模式、装饰器模式等,这些都是解决软件开发中常见问题的成熟方案。 10. **Java框架**...

    张龙JAVA圣思园笔记

    笔记可能涵盖了JVM内存模型(堆、栈、方法区、本地方法栈、程序计数器),垃圾收集(GC)机制,类加载过程,以及如何通过JVM参数进行性能调优。 张龙老师的笔记以实战和案例为导向,旨在帮助读者深入理解这些核心...

    宋红康java笔记.rar

    12. **JVM内存模型**:理解Java虚拟机的工作原理,包括堆内存、栈内存、方法区等,以及垃圾回收机制。 13. **Spring框架**:如果笔记涵盖了Spring,那么会讲解依赖注入、AOP、MVC等核心概念,这是Java开发中广泛...

    Java基础尚硅谷宋红康学习笔记

    7. **JVM优化**:理解JVM内存模型(如堆、栈、方法区、本地方法栈等)、垃圾收集机制以及性能调优技巧。 8. **设计模式**:设计模式是解决软件设计中常见问题的通用解决方案,如工厂模式、单例模式、观察者模式、...

    Java开发学习笔记

    Java开发学习笔记主要针对的是初学者,旨在帮助他们掌握Java编程的基础知识。下面将详细讲解Java开发中的核心概念和步骤。 一、Java环境变量设置 在开始Java编程之前,我们需要安装Java Development Kit (JDK)并...

    韩顺平老师java视频课堂笔记

    笔记可能涵盖类加载、内存模型、垃圾回收等话题。 10. **设计模式**:设计模式是软件开发中的最佳实践,如单例模式、工厂模式、装饰器模式等,笔记将解释这些模式的应用和优缺点。 通过韩顺平老师的Java视频课堂...

    java笔记 java笔记 java笔记

    这份"java笔记"涵盖了从基础到进阶的各种Java知识,旨在帮助学习者系统地掌握这门语言。以下是对笔记内容的详细解读: 1. **Java基础知识**: - **语法特性**:Java遵循C++的语法结构,但更加简洁和安全,如自动...

    Java学习笔记(必看经典).pdf

    本知识点的标题为“Java学习笔记(必看经典)”,意味着所整理的内容是针对Java初学者的一系列核心概念和原理的总结。 首先,面向对象编程是Java语言的核心,它与传统的面向过程编程有显著的不同。面向对象编程强调的...

    JAVA学习笔记 林信良

    在内存管理和垃圾回收方面,书中有专门章节讲解Java的内存模型以及自动内存管理机制,解释了如何避免内存泄漏和理解引用类型,这对于优化程序性能和解决内存问题非常有帮助。 书中还详细阐述了异常处理,这是Java...

    JAVA经典教材笔记

    ### JAVA经典教材笔记知识点梳理 #### 第一章:JAVA概述及开发环境搭建 - **Java学习概述** - Java作为一种广泛使用的编程语言,其学习路径包括掌握基本语法、理解面向对象编程思想、熟悉标准库使用等。 - **JAVA...

    Java基础知识笔记.rar

    20. **JVM**:简要了解Java虚拟机的工作原理,包括类加载、字节码执行和内存模型。 这份笔记不仅适合初学者,也对有经验的开发者巩固基础非常有帮助。通过学习这些内容,你可以建立起坚实的Java编程基础,为进一步...

    狂神说java系列笔记(java基础+javaweb+ssm+微服务)全套

    10. **33、JVM探究.pdf**:深入Java虚拟机,理解内存模型、垃圾回收、性能优化等方面的知识。 通过这些内容的学习,读者不仅可以掌握Java编程的基本技能,还能了解到如何在实际项目中运用这些技能,提升自己的软件...

    java学习笔记(java 反射机制 流 内存管理)

    Java内存管理主要涉及到堆内存和栈内存的分配与回收。堆内存主要用于存储对象实例,由垃圾收集器自动管理。垃圾收集器通过可达性分析判断对象是否可被访问,不可达的对象将被回收。JVM提供了几种不同的垃圾收集算法...

    java校招学习笔记

    8. **JVM内存模型**:理解堆、栈、方法区、本地方法栈和程序计数器,以及垃圾回收机制。 9. **设计模式**:常见的23种设计模式,如单例、工厂、观察者等,它们在实际开发中的应用场景。 10. **网络编程**:TCP和...

    Android&Java; 技术笔记 等五本

    面试通常会涉及Java基础(如封装、继承、多态)、集合框架(List、Set、Map的区别与使用)、异常处理、多线程、IO流、反射机制以及JVM内存模型等。了解这些内容有助于应对面试中的技术问题。 最后,深入浅出Java ...

    java思维导图及笔记

    10. **JVM**:Java虚拟机的工作原理,内存模型(堆、栈、方法区等),垃圾回收机制。 11. **JavaWeb**:Servlet、JSP的基本概念,HTTP协议,MVC设计模式,以及JSTL和EL表达式。 12. **框架技术**:如Spring框架的...

    传智博客JAVA基础笔记个人总结

    8. **JVM内存模型**:理解Java虚拟机(JVM)的工作原理对于优化程序性能至关重要。笔记会探讨堆、栈、方法区等内存区域,以及垃圾回收机制。 9. **Java集合框架深入**:这部分可能包括对泛型、并发容器(如...

    java入门笔记.pdf

    最后,文档介绍了JVM的内存模型,包括堆(Heap)和栈(Stack),以及对象在内存中的分配和回收机制。这是Java开发者需要具备的核心知识之一,因为不当的内存管理可能会导致内存泄漏等问题。 整体来看,这份Java入门笔记...

    java笔记java笔试题 java面试题

    11. **JVM**:理解Java虚拟机的工作原理,如类加载机制、内存模型(堆、栈、方法区等)、垃圾收集等。 12. **Java 8及更高版本的新特性**:例如Lambda表达式、函数式接口、Stream API、Optional类等。 在"java...

Global site tag (gtag.js) - Google Analytics