`
IcyFenix
  • 浏览: 358939 次
  • 性别: Icon_minigender_1
  • 来自: 珠海
文章分类
社区版块
存档分类
最新评论

JVM内存管理:深入Java内存区域与OOM

阅读更多

JavaC++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。

 

概述:

对于从事CC++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又是执行最基础工作的劳动人民——拥有每一个对象的“所有权”,又担负着每一个对象生命开始到终结的维护责任。

 

对于Java程序员来说,不需要在为每一个new操作去写配对的delete/free,不容易出现内容泄漏和内存溢出错误,看起来由JVM管理内存一切都很美好。不过,也正是因为Java程序员把内存控制的权力交给了JVM,一旦出现泄漏和溢出,如果不了解JVM是怎样使用内存的,那排查错误将会是一件非常困难的事情。

 

VM运行时数据区域

JVM执行Java程序的过程中,会使用到各种数据区域,这些区域有各自的用途、创建和销毁时间。根据《Java虚拟机规范(第二版)》(下文称VM Spec)的规定,JVM包括下列几个运行时数据区域:

 

1.程序计数器(Program Counter Register):

 

每一个Java线程都有一个程序计数器来用于保存程序执行到当前方法的哪一个指令,对于非Native方法,这个区域记录的是正在执行的VM原语的地址,如果正在执行的是Natvie方法,这个区域则为空(undefined)。此内存区域是唯一一个在VM Spec中没有规定任何OutOfMemoryError情况的区域。

 

2.Java虚拟机栈(Java Virtual Machine Stacks

与程序计数器一样,VM栈的生命周期也是与线程相同。VM栈描述的是Java方法调用的内存模型:每个方法被执行的时候,都会同时创建一个帧(Frame)用于存储本地变量表、操作栈、动态链接、方法出入口等信息。每一个方法的调用至完成,就意味着一个帧在VM栈中的入栈至出栈的过程。在后文中,我们将着重讨论VM栈中本地变量表部分。

经常有人把Java内存简单的区分为堆内存(Heap)和栈内存(Stack),实际中的区域远比这种观点复杂,这样划分只是说明与变量定义密切相关的内存区域是这两块。其中所指的“堆”后面会专门描述,而所指的“栈”就是VM栈中各个帧的本地变量表部分。本地变量表存放了编译期可知的各种标量类型(booleanbytecharshortintfloatlongdouble)、对象引用(不是对象本身,仅仅是一个引用指针)、方法返回地址等。其中longdouble会占用2个本地变量空间(32bit),其余占用1个。本地变量表在进入方法时进行分配,当进入一个方法时,这个方法需要在帧中分配多大的本地变量是一件完全确定的事情,在方法运行期间不改变本地变量表的大小。

VM Spec中对这个区域规定了2中异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果VM栈可以动态扩展(VM Spec中允许固定长度的VM栈),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。

3.本地方法栈(Native Method Stacks

本地方法栈与VM栈所发挥作用是类似的,只不过VM栈为虚拟机运行VM原语服务,而本地方法栈是为虚拟机使用到的Native方法服务。它的实现的语言、方式与结构并没有强制规定,甚至有的虚拟机(譬如Sun Hotspot虚拟机)直接就把本地方法栈和VM栈合二为一。和VM栈一样,这个区域也会抛出StackOverflowErrorOutOfMemoryError异常。


4.Java
堆(Java Heap

对于绝大多数应用来说,Java堆是虚拟机管理最大的一块内存。Java堆是被所有线程共享的,在虚拟机启动时创建。Java堆的唯一目的就是存放对象实例,绝大部分的对象实例都在这里分配。这一点在VM Spec中的描述是:所有的实例以及数组都在堆上分配(原文:The heap is the runtime data area from which memory for all class instances and arrays is allocated),但是在逃逸分析和标量替换优化技术出现后,VM Spec的描述就显得并不那么准确了。

Java堆内还有更细致的划分:新生代、老年代,再细致一点的:edenfrom survivorto survivor,甚至更细粒度的本地线程分配缓冲(TLAB)等,无论对Java堆如何划分,目的都是为了更好的回收内存,或者更快的分配内存,在本章中我们仅仅针对内存区域的作用进行讨论,Java堆中的上述各个区域的细节,可参见本文第二章《JVM内存管理:深入垃圾收集器与内存分配策略》。

根据VM Spec的要求,Java堆可以处于物理上不连续的内存空间,它逻辑上是连续的即可,就像我们的磁盘空间一样。实现时可以选择实现成固定大小的,也可以是可扩展的,不过当前所有商业的虚拟机都是按照可扩展来实现的(通过-Xmx-Xms控制)。如果在堆中无法分配内存,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

5.方法区(Method Area

叫“方法区”可能认识它的人还不太多,如果叫永久代(Permanent Generation)它的粉丝也许就多了。它还有个别名叫做Non-Heap(非堆),但是VM Spec上则描述方法区为堆的一个逻辑部分(原文:the method area is logically part of the heap),这个名字的问题还真容易令人产生误解,我们在这里就不纠结了。

方法区中存放了每个Class的结构信息,包括常量池、字段描述、方法描述等等。VM Space描述中对这个区域的限制非常宽松,除了和Java堆一样不需要连续的内存,也可以选择固定大小或者可扩展外,甚至可以选择不实现垃圾收集。相对来说,垃圾收集行为在这个区域是相对比较少发生的,但并不是某些描述那样永久代不会发生GC(至少对当前主流的商业JVM实现来说是如此),这里的GC主要是对常量池的回收和对类的卸载,虽然回收的“成绩”一般也比较差强人意,尤其是类卸载,条件相当苛刻。

6.运行时常量池(Runtime Constant Pool

Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量表(constant_pool table),用于存放编译期已可知的常量,这部分内容将在类加载后进入方法区(永久代)存放。但是Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)。

运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法在申请到内存时会抛出OutOfMemoryError异常。

 

7.本机直接内存(Direct Memory

直接内存并不是虚拟机运行时数据区的一部分,它根本就是本机内存而不是VM直接管理的区域。但是这部分内存也会导致OutOfMemoryError异常出现,因此我们放到这里一起描述。

JDK1.4中新加入了NIO类,引入一种基于渠道与缓冲区的I/O方式,它可以通过本机Native函数库直接分配本机内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java对和本机堆中来回复制数据。

显然本机直接内存的分配不会受到Java堆大小的限制,但是即然是内存那肯定还是要受到本机物理内存(包括SWAP区或者Windows虚拟内存)的限制的,一般服务器管理员配置JVM参数时,会根据实际内存设置-Xmx等参数信息,但经常忽略掉直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),而导致动态扩展时出现OutOfMemoryError异常。

 

实战OutOfMemoryError

上述区域中,除了程序计数器,其他在VM Spec中都描述了产生OutOfMemoryError(下称OOM)的情形,那我们就实战模拟一下,通过几段简单的代码,令对应的区域产生OOM异常以便加深认识,同时初步介绍一些与内存相关的虚拟机参数。下文的代码都是基于Sun Hotspot虚拟机1.6版的实现,对于不同公司的不同版本的虚拟机,参数与程序运行结果可能结果会有所差别。

 

Java

 

Java堆存放的是对象实例,因此只要不断建立对象,并且保证GC Roots到对象之间有可达路径即可产生OOM异常。测试中限制Java堆大小为20M,不可扩展,通过参数-XX:+HeapDumpOnOutOfMemoryError让虚拟机在出现OOM异常的时候Dump出内存映像以便分析。(关于Dump映像文件分析方面的内容,可参见本文第三章《JVM内存管理:深入JVM内存异常分析与调优》。)

清单1JavaOOM测试

/**

 * VM Args-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

 * @author zzm

 */

public class HeapOOM {

 

       static class OOMObject {

       }

 

       public static void main(String[] args) {

              List<OOMObject> list = new ArrayList<OOMObject>();

 

              while (true) {

                     list.add(new OOMObject());

              }

       }

}

 

运行结果:

java.lang.OutOfMemoryError: Java heap space

Dumping heap to java_pid3404.hprof ...

Heap dump file created [22045981 bytes in 0.663 secs]

 

 

VM栈和本地方法栈

 

Hotspot虚拟机并不区分VM栈和本地方法栈,因此-Xoss参数实际上是无效的,栈容量只由-Xss参数设定。关于VM栈和本地方法栈在VM Spec描述了两种异常:StackOverflowErrorOutOfMemoryError,当栈空间无法继续分配分配时,到底是内存太小还是栈太大其实某种意义上是对同一件事情的两种描述而已,在笔者的实验中,对于单线程应用尝试下面3种方法均无法让虚拟机产生OOM,全部尝试结果都是获得SOF异常。

 

1.使用-Xss参数削减栈内存容量。结果:抛出SOF异常时的堆栈深度相应缩小。

2.定义大量的本地变量,增大此方法对应帧的长度。结果:抛出SOF异常时的堆栈深度相应缩小。

3.创建几个定义很多本地变量的复杂对象,打开逃逸分析和标量替换选项,使得JIT编译器允许对象拆分后在栈中分配。结果:实际效果同第二点。

 

清单2VM栈和本地方法栈OOM测试(仅作为第1点测试程序)

/**

 * VM Args-Xss128k

 * @author zzm

 */

public class JavaVMStackSOF {

 

       private int stackLength = 1;

 

       public void stackLeak() {

              stackLength++;

              stackLeak();

       }

 

       public static void main(String[] args) throws Throwable {

              JavaVMStackSOF oom = new JavaVMStackSOF();

              try {

                     oom.stackLeak();

              } catch (Throwable e) {

                     System.out.println("stack length:" + oom.stackLength);

                     throw e;

              }

       }

}

 

运行结果:

stack length:2402

Exception in thread "main" java.lang.StackOverflowError

        at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:20)

        at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21)

        at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21)

 

如果在多线程环境下,不断建立线程倒是可以产生OOM异常,但是基本上这个异常和VM栈空间够不够关系没有直接关系,甚至是给每个线程的VM栈分配的内存越多反而越容易产生这个OOM异常。

 

原因其实很好理解,操作系统分配给每个进程的内存是有限制的,譬如32Windows限制为2GJava堆和方法区的大小JVM有参数可以限制最大值,那剩余的内存为2G(操作系统限制)-Xmx(最大堆)-MaxPermSize(最大方法区),程序计数器消耗内存很小,可以忽略掉,那虚拟机进程本身耗费的内存不计算的话,剩下的内存就供每一个线程的VM栈和本地方法栈瓜分了,那自然每个线程中VM栈分配内存越多,就越容易把剩下的内存耗尽。

 

清单3:创建线程导致OOM异常

/**

 * VM Args-Xss2M (这时候不妨设大些)

 * @author zzm

 */

public class JavaVMStackOOM {

 

       private void dontStop() {

              while (true) {

              }

       }

 

       public void stackLeakByThread() {

              while (true) {

                     Thread thread = new Thread(new Runnable() {

                            @Override

                            public void run() {

                                   dontStop();

                            }

                     });

                     thread.start();

              }

       }

 

       public static void main(String[] args) throws Throwable {

              JavaVMStackOOM oom = new JavaVMStackOOM();

              oom.stackLeakByThread();

       }

}

 

特别提示一下,如果读者要运行上面这段代码,记得要存盘当前工作,上述代码执行时有很大令操作系统卡死的风险。

 

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread



运行时常量池

 

要在常量池里添加内容,最简单的就是使用String.intern()这个Native方法。由于常量池分配在方法区内,我们只需要通过-XX:PermSize-XX:MaxPermSize限制方法区大小即可限制常量池容量。实现代码如下:

 

清单4:运行时常量池导致的OOM异常

/**

 * VM Args-XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */

public class RuntimeConstantPoolOOM {

 

       public static void main(String[] args) {

              // 使用List保持着常量池引用,压制Full GC回收常量池行为

              List<String> list = new ArrayList<String>();

              // 10MPermSizeinteger范围内足够产生OOM

              int i = 0;

              while (true) {

                     list.add(String.valueOf(i++).intern());

              }

       }

}

 

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

       at java.lang.String.intern(Native Method)

       at org.fenixsoft.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)

 

 

方法区

 

上文讲过,方法区用于存放Class相关信息,所以这个区域的测试我们借助CGLib直接操作字节码动态生成大量的Class,值得注意的是,这里我们这个例子中模拟的场景其实经常会在实际应用中出现:当前很多主流框架,如SpringHibernate对类进行增强时,都会使用到CGLib这类字节码技术,当增强的类越多,就需要越大的方法区用于保证动态生成的Class可以加载入内存。

 

清单5:借助CGLib使得方法区出现OOM异常

/**

 * VM Args -XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */

public class JavaMethodAreaOOM {

 

       public static void main(String[] args) {

              while (true) {

                     Enhancer enhancer = new Enhancer();

                     enhancer.setSuperclass(OOMObject.class);

                     enhancer.setUseCache(false);

                     enhancer.setCallback(new MethodInterceptor() {

                            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

                                   return proxy.invokeSuper(obj, args);

                            }

                     });

                     enhancer.create();

              }

       }

 

       static class OOMObject {

 

       }

}

 

运行结果:

Caused by: java.lang.OutOfMemoryError: PermGen space

       at java.lang.ClassLoader.defineClass1(Native Method)

       at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)

       at java.lang.ClassLoader.defineClass(ClassLoader.java:616)

       ... 8 more

 

本机直接内存

 

DirectMemory容量可通过-XX:MaxDirectMemorySize指定,不指定的话默认与Java堆(-Xmx指定)一样,下文代码越过了DirectByteBuffer,直接通过反射获取Unsafe实例进行内存分配(Unsafe类的getUnsafe()方法限制了只有引导类加载器才会返回实例,也就是基本上只有rt.jar里面的类的才能使用),因为DirectByteBuffer也会抛OOM异常,但抛出异常时实际上并没有真正向操作系统申请分配内存,而是通过计算得知无法分配既会抛出,真正申请分配的方法是unsafe.allocateMemory()

 

/**

 * VM Args-Xmx20M -XX:MaxDirectMemorySize=10M

 * @author zzm

 */

public class DirectMemoryOOM {

 

       private static final int _1MB = 1024 * 1024;

 

       public static void main(String[] args) throws Exception {

              Field unsafeField = Unsafe.class.getDeclaredFields()[0];

              unsafeField.setAccessible(true);

              Unsafe unsafe = (Unsafe) unsafeField.get(null);

              while (true) {

                     unsafe.allocateMemory(_1MB);

              }

       }

}

 

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError

       at sun.misc.Unsafe.allocateMemory(Native Method)

       at org.fenixsoft.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:20)

 

 

总结

到此为止,我们弄清楚虚拟机里面的内存是如何划分的,哪部分区域,什么样的代码、操作可能导致OOM异常。虽然Java有垃圾收集机制,但OOM仍然离我们并不遥远,本章内容我们只是知道各个区域OOM异常出现的原因,下一章我们将看看Java垃圾收集机制为了避免OOM异常出现,做出了什么样的努力。

分享到:
评论
41 楼 sessionsong 2014-02-14  
neo_q 写道
/**

 * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */

public class RuntimeConstantPoolOOM {

 

       public static void main(String[] args) {

              // 使用List保持着常量池引用,压制Full GC回收常量池行为

              List<String> list = new ArrayList<String>();

              // 10M的PermSize在integer范围内足够产生OOM了

              int i = 0; 

              while (true) {

                     list.add(String.valueOf(i++).intern());//个人认为在此种特殊情况下,你只要创建了新的String对象常量池都会产生新的对象,是否调用intern方法是不会有影响的,因为intern方法会查找常量池中已经存在的字符串,如果有存在就直接返回,如果不存在才会进一步锁住常量池进行添加的行为
              }

       }

}


intern 方法 如果存在那么就返回 不存在的话就会新建一个字符串放在常量池中
没有使用这个方法的话 String.valueOf(i++) 会返回一个string对象实例(可以看源码) 而实例是存放在java heap中的 因为这是一个无限循环 所以最后会出现堆异常。
使用的话 出现的是常量池异常
下面是报错信息:
没有使用 intern 方法:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Unknown Source)
at java.lang.Integer.toString(Unknown Source)
at java.lang.String.valueOf(Unknown Source)
at com.sxl.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:22)

调用了intern方法:
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at com.sxl.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:33)

40 楼 11122233 2011-06-08  
赞一个,需要这样的讲jvm的文章。
39 楼 独爱Java 2011-04-14  
首先,赞楼主功力深厚,最近看了一篇文章,上面提到了不同的观点,后来咨询了许多人,得到的答案也是不一致,心里一直在纠结着,希望得到楼主以及其他前辈的解答。

问题引出:类变量是在java虚拟机启动后就被初始化好了???

//注:个人最近在接触和学习java底层的一些知识,通过看了网上的许多文章,有了下面的一段想法,但无奈,网上文章议论不一,自己心里又不确定。所以请教楼主指出问题,我好接着努力。

  类变量(静态变量),它是不和某个具体的对象绑定在一起的,是属于类的;当需要使用到该类时,就会被加载到内存中,静态变量就会被初始化。也就是说,一个应用程序执行了(底层实例化好了一个java虚拟机实例,支持着这个应用程序的执行),但是并不一定就初始化好了各个类的静态成员变量。//支持错误的观点。

38 楼 风中的树叶 2011-03-04  
学习了。写的很好。
37 楼 neo_q 2011-01-25  
/**

 * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M

 * @author zzm

 */

public class RuntimeConstantPoolOOM {

 

       public static void main(String[] args) {

              // 使用List保持着常量池引用,压制Full GC回收常量池行为

              List<String> list = new ArrayList<String>();

              // 10M的PermSize在integer范围内足够产生OOM了

              int i = 0; 

              while (true) {

                     list.add(String.valueOf(i++).intern());//个人认为在此种特殊情况下,你只要创建了新的String对象常量池都会产生新的对象,是否调用intern方法是不会有影响的,因为intern方法会查找常量池中已经存在的字符串,如果有存在就直接返回,如果不存在才会进一步锁住常量池进行添加的行为
              }

       }

}
36 楼 sleepinglord 2011-01-14  
如果能支持自己真的delete对象以及强制调用gc就好了。
35 楼 xiaomogui 2011-01-04  
楼主,多写点类似的文章,好文.呵呵
34 楼 IcyFenix 2010-12-22  
asle 写道
IcyFenix 写道
vtudiv 写道
其中long和double会占用2个本地变量空间(32bit),其余占用1个。
这句话什么意思?


根据VM Spec的规定,超过32位大小的数据,VM是把他们当做2个32位数据代替,每次get/set其实都执行了2次操作,而小于等于32位的数据类型,只需要执行1次。因此小于等于32位的类型访问是原子操作,而long和double的访问是非原子操作,这个在并发程序中需要注意,可以通过volatile关键字来保证原子性。

上述描述对32位虚拟机有效,64位未做过研究,不评论。


java中对一个变量的读写肯定是原子操作的,不会出现只到到一半就被中断的情况


如果你说的是特定某种虚拟机实现,那这个问题可以继续讨论,因为具体实现中可以选择把这个操作实现为原子的,这点是允许的(“An implementation is free to implement load, store, read, and write operations for double and long values as atomic 64-bit operations”)。

但如果你说的是仅仅是“java中”,那我想这个问题还是遵循虚拟机规范来的妥当一些。

在虚拟机规范第二版8.4节“Nonatomic Treatment of double and long Variables”中关于double和long变量描述如下:

引用

If a double or long variable is not declared volatile, then for the purposes of load, store, read, and write operations it is treated as if it were two variables of 32 bits each; wherever the rules require one of these operations, two such operations are performed, one for each 32-bit half. The manner in which the 64 bits of a double or long variable are encoded into two 32-bit quantities and the order of the operations on the halves of the variables are not defined by The Java  Language Specification.

This matters only because a read or write of a double or long variable may be handled by an actual main memory as two 32-bit read or write operations that may be separated in time, with other operations coming between them. Consequently, if two threads concurrently assign distinct values to the same shared non-volatile double or long variable, a subsequent use of that variable may obtain a value that is not equal to either of the assigned values, but rather some implementation-dependent mixture of the two values.


33 楼 asle 2010-12-22  
IcyFenix 写道
vtudiv 写道
其中long和double会占用2个本地变量空间(32bit),其余占用1个。
这句话什么意思?


根据VM Spec的规定,超过32位大小的数据,VM是把他们当做2个32位数据代替,每次get/set其实都执行了2次操作,而小于等于32位的数据类型,只需要执行1次。因此小于等于32位的类型访问是原子操作,而long和double的访问是非原子操作,这个在并发程序中需要注意,可以通过volatile关键字来保证原子性。

上述描述对32位虚拟机有效,64位未做过研究,不评论。

java中对一个变量的读写肯定是原子操作的,不会出现只到到一半就被中断的情况
32 楼 alanlhy 2010-12-21  
关于大家所说的这些vm内存分配,我有些不懂,还请大家指教。我们老师再给我们做内存分析的时候,所java在内存中分为heap、stack、data segment、code segment四部分,而heap是放对象等new出来的东西,而stack则是放引用和局部变量的内存块,data怎是放类变量和字符串常量的,code是放代码的内存。而大家所说的常量池、方法区等等是怎么分的,求给为详解。
31 楼 umeit 2010-12-14  
在VM Spec中对这个区域规定了2中异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果VM栈可以动态扩展(VM Spec中允许固定长度的VM栈),当扩展时无法申请到足够内存则抛出OutOfMemoryError异常。
-------------------------------------------------------------------------
是否详细的讲一下这段?
30 楼 nighthawk 2010-12-13  
非常棒,实例能让人更好的理解
29 楼 wfly0001 2010-11-15  
好文章啊,尤其是对oom的解释和分类
28 楼 vyloy 2010-11-13  
赞,楼主的功力深厚~
27 楼 codermouse 2010-11-10  
太深入了。待了解。
26 楼 wwwesdt34 2010-11-10  
拜读 慢慢研究
25 楼 vtudiv 2010-11-08  
IcyFenix 写道
vtudiv 写道
其中long和double会占用2个本地变量空间(32bit),其余占用1个。
这句话什么意思?


根据VM Spec的规定,超过32位大小的数据,VM是把他们当做2个32位数据代替,每次get/set其实都执行了2次操作,而小于等于32位的数据类型,只需要执行1次。因此小于等于32位的类型访问是原子操作,而long和double的访问是非原子操作,这个在并发程序中需要注意,可以通过volatile关键字来保证原子性。

上述描述对32位虚拟机有效,64位未做过研究,不评论。

非常感谢~
24 楼 IcyFenix 2010-11-07  
<div class="quote_title">fantasy 写道</div>
<div class="quote_div">
<p><br>标量类型? 英文是什么?我们一般叫基本类型吧?</p>
<p> </p>
</div>
<p> </p>
<p><span style="font-family: Arial, sans-serif, Helvetica, Tahoma; line-height: 18px;">Scalar Type</span></p>
<p> </p>
<p>这个翻译应该没有问题,譬如说"<span style="font-family: Arial, sans-serif, Helvetica, Tahoma; line-height: 18px;">Scalar Replacement",也许“基本类型替换”会来的跟好懂一些,但翻译为"标量替换"应该更符合习惯。</span></p>
<p> </p>
<p>这篇文章纠结于英文词汇在中文中怎么说这很无趣。</p>
<p> </p>
<p>刚刚有同学发现了个问题,PM给我,这里修正一下,感谢renwolang521同学</p>
<p> </p>
<p> </p>
<p>
</p>
<div class="quote_div" style="margin-top: 0px; margin-right: 5px; margin-bottom: 5px; margin-left: 15px; background-color: #fafafa; padding: 3px; border: 1px solid #cccccc;">
<p style="margin: 0px;"><br><span style="">规则一:-XX:SurvivorRatio=8决定了新生代中eden与survivor的空间比例是<span style="color: #ff0000;"><strong>1:8 </strong></span></span></p>
<p style="margin: 0px;"> </p>
</div>

<p> </p>
<p>这里有个错误,应该是8:1</p>
23 楼 fantasy 2010-11-07  
<div class="quote_title">IcyFenix 写道</div>
<div class="quote_div">
<div class="quote_title">sswh 写道</div>
<div class="quote_div">
<p> </p>
<p>     <span style="color: #ff0000;">本地变量表</span>   一般的叫法是  <span style="color: #ff0000;">局部变量表</span> 吧?</p>
<p> </p>
<p>     是翻译还是原著,表达方式怎么这么奇怪。</p>
<p> </p>
</div>
<p> </p>
<p>Local Variables</p>
<p> </p>
<p>这个中文有什么很权威的翻译吗?日后我注意一下,谢谢。</p>
</div>
<p>Local Variables   翻译为 局部变量</p>
<p>native 翻译为  本地</p>
<p>标量类型? 英文是什么?我们一般叫基本类型吧?</p>
<p> </p>
22 楼 niyunjiu 2010-11-06  
very good!

相关推荐

Global site tag (gtag.js) - Google Analytics