- 浏览: 209357 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
Prepared:
Hadoop的几个明显缺点 -
CSunDNan:
...
openjdk jvm 方法字节码执行过程 -
幻影之蚀:
...
mysql 源码分析2 源码调试环境建立 -
shukongchengje:
紧急呼唤楼主,mysql代码从哪里弄?官网wiki上看的一头雾 ...
mysql源码分析 整体架构 -
yeshaoting:
好文章.不介意的话转载了.
jvm 字节码中文含义
Java 平台把线程和多处理技术集成到了语言中,这种集成程度比以前的大多数编程语言都要强很多。该语言对于平台独立的并发及多线程技术的支持是野心勃勃并且是具有开拓性的,或许并不奇怪,这个问题要比 Java 体系结构设计者的原始构想要稍微困难些。关于同步和线程安全的许多底层混淆是 Java 内存模型 (JMM)的一些难以直觉到的细微差别,这些差别最初是在 Java Language Specification 的第 17 章中指定的,并且由 JSR 133 重新指定。
例如,并不是所有的多处理器系统都表现出缓存一致性(cache coherency);假如有一个处理器有一个更新了的变量值位于其缓存中,但还没有被存入主存,这样别的处理器就可能会看不到这个更新的值。在缓存缺乏一致性的情况下,两个不同的处理器可以看到在内存中同一位置处有两种不同的值。这听起来不太可能,但是这却是故意的 —— 这是一种获得较高的性能和可伸缩性的方法 —— 但是这加重了开发者和编译器为解决这些问题而编写代码的负担。
什么是内存模型,我为什么需要一个内存模型?
内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。对象最终存储在内存中,但编译器、运行库、处理器或缓存可以有特权定时地在变量的指定内存位置存入或取出变量值。例如,编译器为了优化一个循环索引变量,可能会选择把它存储到一个寄存器中,或者缓存会延迟到一个更适合的时间,才把一个新的变量值存入主存。所有的这些优化是为了帮助实现更高的性能,通常这对于用户来说是透明的,但是对多处理系统来说,这些复杂的事情可能有时会完全显现出来。
JMM 允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员已经使用synchronized 或final明确地请求了某些可见性保证。这意味着在缺乏同步的情况下,从不同的线程角度来看,内存的操作是以不同的次序发生的。
与之相对应地,像 C 和 C++ 这些语言就没有显示的内存模型 —— 但 C 语言程序继承了执行程序处理器的内存模型(尽管一个给定体系结构的编译器可能知道有关底层处理器的内存模型的一些情况,并且保持一致性的一部分责任也落到了该编译器的头上)。这意味着并发的 C 语言程序可以在一个,而不能在另一个,处理器体系结构上正确地运行。虽然一开始 JMM 会有些混乱,但这有个很大的好处 —— 根据 JMM 而被正确同步的程序能正确地运行在任何支持 Java 的平台上。
原始 JMM 的缺点
虽然在 Java Language Specification 的第 17 章指定的 JMM 是一个野心勃勃的尝试,它尝试定义一个一致的、跨平台的内存模型,但它有一些细微而重要的缺点。synchronized和volatile的语义很让人混淆,以致于许多有见地的开发者有时选择忽略这些规则,因为在旧的存储模型下编写正确同步的代码非常困难。
旧的 JMM 允许一些奇怪而混乱的事情发生,比如 final 字段看起来没有那种设置在构造函数里的值(这样使得想像上的不可变对象并不是不可变的)和内存操作重新排序的意外结果。这也防止了其他一些有效的编译器优化。如果您阅读了关于双重检查锁定问题(double-checked locking problem)的任何文章,您将会记得内存操作重新排序是多么的混乱,以及当您没有正确地同步(或者没有积极地试图避免同步)时,细微却严重的问题会如何暗藏在您的代码中。更糟糕的是,许多没有正确同步的程序在某些情况下似乎工作得很好,例如在轻微的负载下、在单处理器系统上,或者在具有比 JMM 所要求的更强的内存模型的处理器上。
“重新排序”这个术语用于描述几种对内存操作的真实明显的重新排序的类型:
当编译器不会改变程序的语义时,作为一种优化它可以随意地重新排序某些指令。
在某些情况下,可以允许处理器以颠倒的次序执行一些操作。
通常允许缓存以与程序写入变量时所不相同的次序把变量存入主存。
从另一线程的角度来看,任何这些条件都会引发一些操作以不同于程序指定的次序发生 —— 并且忽略重新排序的源代码时,内存模型认为所有这些条件都是同等的。
JSR 133 的目标
JSR 133 被授权来修复 JMM,它有几个目标:
保留现有的安全保证,包括类型安全。
提供无中生有安全性(out-of-thin-air safety)。这意味着变量值并不是“无中生有”地创建的 —— 所以对于一个线程来说,要观察到一个变量具有变量值 X,必须有某个线程以前已经真正把变量值 X 写入了那个变量。
“正确同步的”程序的语义应该尽可能简单直观。这样,“正确同步的”应该被正式而直观地定义(这两种定义应该相互一致)。
程序员应该要有信心创建多线程程序。当然,我们没有魔法使得编写并发程序变得很容易,但是我们的目标是为了减轻程序员理解内存模型所有细节的负担。
跨大范围的流行硬件体系结构上的高性能 JVM 实现应该是可能的。现代的处理器在它们的内存模型上有着很大的不同;JMM 应该能够适合于实际的尽可能多的体系结构,而不会以牺牲性能为代价。
提供一个同步习惯用法(idiom),以允许我们发布一个对象并且使得它不用同步就可见。这是一种叫做初始化安全(initialization safety)的新的安全保证。
对现有代码应该只有最小限度的影响。
值得注意的是,有漏洞的技术(如双重检查锁定)在新的内存模型下仍然有漏洞,并且“修复”双重检查锁定技术并不是新内存模型所致力的一个目标。(但是,volatile的新语义允许通常所提出的其中一个双重检查锁定的可选方法正确地工作,尽管我们不鼓励这种技术。)
从 JSR 133 process 变得活跃的三年来,人们发现这些问题比他们认为重要的任何问题都要微妙得多。这就是作为一个开拓者的代价!最终正式的语义比原来所预料的要复杂得多,实际上它采用了一种与原先预想的完全不同的形式,但非正式的语义是清晰直观的,将在本文的第 2 部分概要地说明。
同步和可见性
大多数程序员都知道,synchronized 关键字强制实施一个互斥锁(互相排斥),这个互斥锁防止每次有多个线程进入一个给定监控器所保护的同步语句块。但是同步还有另一个方面:正如 JMM 所指定,它强制实施某些内存可见性规则。它确保了当存在一个同步块时缓存被更新,当输入一个同步块时缓存失效。因此,在一个由给定监控器保护的同步块期间,一个线程所写入的值对于其余所有的执行由同一监控器所保护的同步块的线程来说是可见的。它也确保了编译器不会把指令从一个同步块的内部移到外部(虽然在某些情况下它会把指令从同步块的外部移到内部)。JMM 在缺乏同步的情况下不会做这种保证 —— 这就是只要有多个线程访问相同的变量时必须使用同步(或者它的同胞,易失性)的原因。
问题 1:不可变对象不是不可变的
JMM 的其中一个最惊人的缺点是,不可变对象似乎可以改变它们的值(这种对象的不变性旨在通过使用final 关键字来得到保证)。(Public Service Reminder:让一个对象的所有字段都为final并不一定使得这个对象不可变 —— 所有的字段还必须是原语类型或是对不可变对象的引用。)不可变对象(如String)被认为不要求同步。但是,因为在将内存写方面的更改从一个线程传播到另一个线程时存在潜在的延迟,所以有可能存在一种竞态条件,即允许一个线程首先看到不可变对象的一个值,一段时间之后看到的是一个不同的值。
这是怎么发生的呢?考虑到 Sun 1.4 JDK 中String的实现,这儿基本上有三个重要的决定性字段:对字符数组的引用、长度和描述字符串开始的字符数组的偏移量。String是以这种方式实现的,而不是只有字符数组,因此字符数组可以在多个String和StringBuffer 对象之间共享,而不需要在每次创建一个String时都将文本拷贝到一个新的数组里。例如,String.substring()创建了一个可以与原始的String共享同一个字符数组的新字符串,并且这两个字符串仅仅只是在长度和偏移量上有所不同。
假设您执行以下的代码:
String s1 = "/usr/tmp";String s2 = s1.substring(4); // contains "/tmp"
字符串s2将具有大小为 4 的长度和偏移量,但是它将同s1共享包含“ /usr/tmp ”的同一字符数组。在String构造函数运行之前,Object的构造函数将用它们默认的值初始化所有字段,包括决定性的长度和偏移字段。当String构造器运行时,字符串长度和偏移量被设置成所需要的值。但是在旧的内存模型下,在缺乏同步的情况下,有可能另一个线程会临时地看到偏移量字段具有初默认值 0,而后又看到正确的值 4。结果是s2的值从“ /usr ”变成了“ /tmp ”。这并不是我们所想要的,而且在所有 JVM 或平台这是不可能的,但是旧的内存模型规范允许这样做。
问题 2:重新排序易失性和非易失性存储
另一个主要领域是与volatile字段的内存操作重新排序有关,这个领域中现有 JMM 引起了一些非常混乱的结果。现有 JMM 表明易失性的读和写是直接和主存打交道的,这样避免了把值存储到寄存器或者绕过处理器特定的缓存。这使得多个线程一般能看见一个给定变量的最新的值。可是,结果是这种volatile定义并没有最初所想像的那样有用,并且它导致了volatile实际意义上的重大混乱。
为了在缺乏同步的情况下提供较好的性能,编译器、运行库和缓存通常被允许重新排序普通的内存操作,只要当前执行的线程分辨不出它们的区别。(这就是所谓的线程内似乎是串行的语义(within-thread as-if-serial semantics)。)但是,易失性的读和写是完全跨线程安排的,编译器或缓存不能在彼此之间重新排序易失性的读和写。遗憾的是,通过参考普通变量的读和写,JMM 允许易失性的读和写被重新排序,这意味着我们不能使用易失性标志作为操作已完成的指示。考虑下面的代码,其意图是假定易失性字段initialized用于表明初始化已经完成了。
清单 1. 使用一个易失性字段作为一个“守卫”变量
Map configOptions;char[] configText;volatile boolean initialized = false; . .// In thread A
configOptions = new HashMap();configText = readConfigFile(fileName);processConfigOptions(configText, configOptions);initialized = true; . .// In thread B
while (!initialized) sleep();
// use configOptions这里的思想是使用易失性变量initialized担任守卫来表明一套别的操作已经完成了。这是一个很好的思想,但是它不能在旧的 JMM 下工作,因为旧的 JMM 允许非易失性的写(比如写到configOptions字段,以及写到由configOptions引用Map的字段中)与易失性的写一起重新排序,因此另一个线程可能会看到initialized为 true,但是对于configOptions字段或它所引用的对象还没有一个一致的或者说当前的视图。volatile 的旧语义只承诺正在读和写的变量的可见性,而不承诺其他的变量。虽然这种方法更容易有效地实现,但结果是没有原来所想的那么有用。
结束语
正如 Java Language Specification 第 17 章中所指定的,JMM 有一些严重的缺点,即允许一些看起来合理的程序发生一些非直观的或不合需要的事情。如果正确地编写并发的类太困难的话,那么我们可以说许多并发的类不能按预期工作,并且这是平台中的一个缺点。幸运的是,我们可以在不破坏在旧的内存模型下正确同步的任何代码的同时,创建一个与大多数开发者的直觉更加一致的内存模型,并且这一切已经由 JSR 133 process 完成。下个月,我们将介绍新的内存模型(它的大部分功能已集成到 1.4 JDK 中)的详细信息。
例如,并不是所有的多处理器系统都表现出缓存一致性(cache coherency);假如有一个处理器有一个更新了的变量值位于其缓存中,但还没有被存入主存,这样别的处理器就可能会看不到这个更新的值。在缓存缺乏一致性的情况下,两个不同的处理器可以看到在内存中同一位置处有两种不同的值。这听起来不太可能,但是这却是故意的 —— 这是一种获得较高的性能和可伸缩性的方法 —— 但是这加重了开发者和编译器为解决这些问题而编写代码的负担。
什么是内存模型,我为什么需要一个内存模型?
内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。对象最终存储在内存中,但编译器、运行库、处理器或缓存可以有特权定时地在变量的指定内存位置存入或取出变量值。例如,编译器为了优化一个循环索引变量,可能会选择把它存储到一个寄存器中,或者缓存会延迟到一个更适合的时间,才把一个新的变量值存入主存。所有的这些优化是为了帮助实现更高的性能,通常这对于用户来说是透明的,但是对多处理系统来说,这些复杂的事情可能有时会完全显现出来。
JMM 允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,除非程序员已经使用synchronized 或final明确地请求了某些可见性保证。这意味着在缺乏同步的情况下,从不同的线程角度来看,内存的操作是以不同的次序发生的。
与之相对应地,像 C 和 C++ 这些语言就没有显示的内存模型 —— 但 C 语言程序继承了执行程序处理器的内存模型(尽管一个给定体系结构的编译器可能知道有关底层处理器的内存模型的一些情况,并且保持一致性的一部分责任也落到了该编译器的头上)。这意味着并发的 C 语言程序可以在一个,而不能在另一个,处理器体系结构上正确地运行。虽然一开始 JMM 会有些混乱,但这有个很大的好处 —— 根据 JMM 而被正确同步的程序能正确地运行在任何支持 Java 的平台上。
原始 JMM 的缺点
虽然在 Java Language Specification 的第 17 章指定的 JMM 是一个野心勃勃的尝试,它尝试定义一个一致的、跨平台的内存模型,但它有一些细微而重要的缺点。synchronized和volatile的语义很让人混淆,以致于许多有见地的开发者有时选择忽略这些规则,因为在旧的存储模型下编写正确同步的代码非常困难。
旧的 JMM 允许一些奇怪而混乱的事情发生,比如 final 字段看起来没有那种设置在构造函数里的值(这样使得想像上的不可变对象并不是不可变的)和内存操作重新排序的意外结果。这也防止了其他一些有效的编译器优化。如果您阅读了关于双重检查锁定问题(double-checked locking problem)的任何文章,您将会记得内存操作重新排序是多么的混乱,以及当您没有正确地同步(或者没有积极地试图避免同步)时,细微却严重的问题会如何暗藏在您的代码中。更糟糕的是,许多没有正确同步的程序在某些情况下似乎工作得很好,例如在轻微的负载下、在单处理器系统上,或者在具有比 JMM 所要求的更强的内存模型的处理器上。
“重新排序”这个术语用于描述几种对内存操作的真实明显的重新排序的类型:
当编译器不会改变程序的语义时,作为一种优化它可以随意地重新排序某些指令。
在某些情况下,可以允许处理器以颠倒的次序执行一些操作。
通常允许缓存以与程序写入变量时所不相同的次序把变量存入主存。
从另一线程的角度来看,任何这些条件都会引发一些操作以不同于程序指定的次序发生 —— 并且忽略重新排序的源代码时,内存模型认为所有这些条件都是同等的。
JSR 133 的目标
JSR 133 被授权来修复 JMM,它有几个目标:
保留现有的安全保证,包括类型安全。
提供无中生有安全性(out-of-thin-air safety)。这意味着变量值并不是“无中生有”地创建的 —— 所以对于一个线程来说,要观察到一个变量具有变量值 X,必须有某个线程以前已经真正把变量值 X 写入了那个变量。
“正确同步的”程序的语义应该尽可能简单直观。这样,“正确同步的”应该被正式而直观地定义(这两种定义应该相互一致)。
程序员应该要有信心创建多线程程序。当然,我们没有魔法使得编写并发程序变得很容易,但是我们的目标是为了减轻程序员理解内存模型所有细节的负担。
跨大范围的流行硬件体系结构上的高性能 JVM 实现应该是可能的。现代的处理器在它们的内存模型上有着很大的不同;JMM 应该能够适合于实际的尽可能多的体系结构,而不会以牺牲性能为代价。
提供一个同步习惯用法(idiom),以允许我们发布一个对象并且使得它不用同步就可见。这是一种叫做初始化安全(initialization safety)的新的安全保证。
对现有代码应该只有最小限度的影响。
值得注意的是,有漏洞的技术(如双重检查锁定)在新的内存模型下仍然有漏洞,并且“修复”双重检查锁定技术并不是新内存模型所致力的一个目标。(但是,volatile的新语义允许通常所提出的其中一个双重检查锁定的可选方法正确地工作,尽管我们不鼓励这种技术。)
从 JSR 133 process 变得活跃的三年来,人们发现这些问题比他们认为重要的任何问题都要微妙得多。这就是作为一个开拓者的代价!最终正式的语义比原来所预料的要复杂得多,实际上它采用了一种与原先预想的完全不同的形式,但非正式的语义是清晰直观的,将在本文的第 2 部分概要地说明。
同步和可见性
大多数程序员都知道,synchronized 关键字强制实施一个互斥锁(互相排斥),这个互斥锁防止每次有多个线程进入一个给定监控器所保护的同步语句块。但是同步还有另一个方面:正如 JMM 所指定,它强制实施某些内存可见性规则。它确保了当存在一个同步块时缓存被更新,当输入一个同步块时缓存失效。因此,在一个由给定监控器保护的同步块期间,一个线程所写入的值对于其余所有的执行由同一监控器所保护的同步块的线程来说是可见的。它也确保了编译器不会把指令从一个同步块的内部移到外部(虽然在某些情况下它会把指令从同步块的外部移到内部)。JMM 在缺乏同步的情况下不会做这种保证 —— 这就是只要有多个线程访问相同的变量时必须使用同步(或者它的同胞,易失性)的原因。
问题 1:不可变对象不是不可变的
JMM 的其中一个最惊人的缺点是,不可变对象似乎可以改变它们的值(这种对象的不变性旨在通过使用final 关键字来得到保证)。(Public Service Reminder:让一个对象的所有字段都为final并不一定使得这个对象不可变 —— 所有的字段还必须是原语类型或是对不可变对象的引用。)不可变对象(如String)被认为不要求同步。但是,因为在将内存写方面的更改从一个线程传播到另一个线程时存在潜在的延迟,所以有可能存在一种竞态条件,即允许一个线程首先看到不可变对象的一个值,一段时间之后看到的是一个不同的值。
这是怎么发生的呢?考虑到 Sun 1.4 JDK 中String的实现,这儿基本上有三个重要的决定性字段:对字符数组的引用、长度和描述字符串开始的字符数组的偏移量。String是以这种方式实现的,而不是只有字符数组,因此字符数组可以在多个String和StringBuffer 对象之间共享,而不需要在每次创建一个String时都将文本拷贝到一个新的数组里。例如,String.substring()创建了一个可以与原始的String共享同一个字符数组的新字符串,并且这两个字符串仅仅只是在长度和偏移量上有所不同。
假设您执行以下的代码:
String s1 = "/usr/tmp";String s2 = s1.substring(4); // contains "/tmp"
字符串s2将具有大小为 4 的长度和偏移量,但是它将同s1共享包含“ /usr/tmp ”的同一字符数组。在String构造函数运行之前,Object的构造函数将用它们默认的值初始化所有字段,包括决定性的长度和偏移字段。当String构造器运行时,字符串长度和偏移量被设置成所需要的值。但是在旧的内存模型下,在缺乏同步的情况下,有可能另一个线程会临时地看到偏移量字段具有初默认值 0,而后又看到正确的值 4。结果是s2的值从“ /usr ”变成了“ /tmp ”。这并不是我们所想要的,而且在所有 JVM 或平台这是不可能的,但是旧的内存模型规范允许这样做。
问题 2:重新排序易失性和非易失性存储
另一个主要领域是与volatile字段的内存操作重新排序有关,这个领域中现有 JMM 引起了一些非常混乱的结果。现有 JMM 表明易失性的读和写是直接和主存打交道的,这样避免了把值存储到寄存器或者绕过处理器特定的缓存。这使得多个线程一般能看见一个给定变量的最新的值。可是,结果是这种volatile定义并没有最初所想像的那样有用,并且它导致了volatile实际意义上的重大混乱。
为了在缺乏同步的情况下提供较好的性能,编译器、运行库和缓存通常被允许重新排序普通的内存操作,只要当前执行的线程分辨不出它们的区别。(这就是所谓的线程内似乎是串行的语义(within-thread as-if-serial semantics)。)但是,易失性的读和写是完全跨线程安排的,编译器或缓存不能在彼此之间重新排序易失性的读和写。遗憾的是,通过参考普通变量的读和写,JMM 允许易失性的读和写被重新排序,这意味着我们不能使用易失性标志作为操作已完成的指示。考虑下面的代码,其意图是假定易失性字段initialized用于表明初始化已经完成了。
清单 1. 使用一个易失性字段作为一个“守卫”变量
Map configOptions;char[] configText;volatile boolean initialized = false; . .// In thread A
configOptions = new HashMap();configText = readConfigFile(fileName);processConfigOptions(configText, configOptions);initialized = true; . .// In thread B
while (!initialized) sleep();
// use configOptions这里的思想是使用易失性变量initialized担任守卫来表明一套别的操作已经完成了。这是一个很好的思想,但是它不能在旧的 JMM 下工作,因为旧的 JMM 允许非易失性的写(比如写到configOptions字段,以及写到由configOptions引用Map的字段中)与易失性的写一起重新排序,因此另一个线程可能会看到initialized为 true,但是对于configOptions字段或它所引用的对象还没有一个一致的或者说当前的视图。volatile 的旧语义只承诺正在读和写的变量的可见性,而不承诺其他的变量。虽然这种方法更容易有效地实现,但结果是没有原来所想的那么有用。
结束语
正如 Java Language Specification 第 17 章中所指定的,JMM 有一些严重的缺点,即允许一些看起来合理的程序发生一些非直观的或不合需要的事情。如果正确地编写并发的类太困难的话,那么我们可以说许多并发的类不能按预期工作,并且这是平台中的一个缺点。幸运的是,我们可以在不破坏在旧的内存模型下正确同步的任何代码的同时,创建一个与大多数开发者的直觉更加一致的内存模型,并且这一切已经由 JSR 133 process 完成。下个月,我们将介绍新的内存模型(它的大部分功能已集成到 1.4 JDK 中)的详细信息。
发表评论
-
Java类的初始化过程详解
2010-04-11 17:09 1218java是多线程的,所以类 ... -
jvm 字节码中文含义
2010-04-08 11:31 3611栈和局部变量操作 将常 ... -
诡异的JVM Crash之EXCEPTION_STACK_OVERFLOW
2010-04-08 11:08 1730关于JVM的一个常识是:如果对象被意外地持有很容易造成内存泄漏 ... -
jvm 初始化
2010-04-07 16:01 1166void vm_init_globals() { chec ... -
jvm gc
2010-04-06 23:52 1008jvm gc -
jvm exception
2010-04-06 23:52 846jvm exception -
jvm method invoke
2010-04-06 23:51 1067jvm method invoke -
jvm interpreter
2010-04-06 23:51 1240jvm interpreter 解释器的初始化 JNI_Cr ... -
jvm运行时数据结构
2010-04-06 23:50 900jvm运行时数据结构 -
jvm class与instance运行时结构
2010-04-06 23:49 862jvm class与instance运行时结构 -
jvm dll加载分析
2010-04-06 23:48 1160jvm dll加载分析 -
java class文件格式
2010-04-06 23:43 1419struct ClassFile { u4 magic; ... -
汇编语言与计算机体系结构--使用C++和Java
2010-04-06 15:16 1667第1章数制1 1.1简介1 1.2按位记数制1 1.3各数制的 ... -
JVM内存管理及垃圾回收机制
2010-04-06 09:30 846JAVA在内存中由 Perm 和 He ... -
IBM Java Jvm GC实现内幕 (几年前的老文)
2010-04-05 14:39 824好言归正传,按照Sam Borman的说法IBM java 1 ... -
JVM内存结构
2010-03-29 15:25 796(1)类装载子系统 装载 连 ... -
Java轻量级锁原理详解(Lightweight Locking)
2010-03-29 15:21 783Java轻量级锁原理详解(Lightweight Lockin ... -
解析 Java 类和对象的初始化过程
2010-03-29 15:15 1056类的初始化和对象初始 ... -
解析 Java 类和对象的初始化过程
2010-03-29 15:14 735类的初始化和对象初始化是 JVM 管理的类型生命周期中非常重要 ... -
Java偏向锁实现原理(Biased Locking)
2010-03-29 15:00 881Java偏向锁实现原理(Bias ...
相关推荐
### Java理论与实践:修复Java内存模型,第1部分 #### 概述 活跃了将近三年的JSR133近期发布了一项关于如何修复Java内存模型(Java Memory Model, JMM)的重要建议。原始JMM中存在一些严重的缺陷,这导致了原本被...
尽管Java最初被认为是解释型语言,但随着时间的发展,其性能得到了极大的优化。现代的JVM采用了即时编译(Just-In-Time Compilation, JIT)技术,能够将Java字节码转换为本地机器码,从而大幅提升程序的执行速度。...
VNCViewer Java是一款基于Java开发的远程桌面控制工具,它允许用户通过网络连接到其他计算机并进行图形界面的远程操作。这个项目源自Java1.0版本,虽然相对较老,但仍然能够正常工作,并且与VNC服务器兼容。下面将...
Java KCP是一个基于Java语言,利用Netty框架实现的可靠UDP网络库,它引入了KCP协议和Forward Error Correction (FEC)技术,为需要高效、低延迟传输的应用提供了强大的解决方案。KCP协议最初由Mars(巫师)设计,其...
在IT行业中,ICQ是一种历史悠久的即时通讯软件,它的全称是"Internet CQ",最初由以色列的Mirabilis公司开发。这个项目的核心在于创建一个基于JAVA的简单实现,让我们来深入探讨如何使用JAVA来实现ICQ的功能。 首先...
Java是一种面向对象的编程语言,由Sun Microsystems的James Gosling等人在1990年代初期设计,最初是为了在各种网络设备和嵌入式系统上开发先进的软件。它旨在解决异构、跨网络分布式环境中的应用开发挑战,尤其注重...
该版本为2.6.6,意味着它是该客户端的一个稳定版本,可能包含了之前版本的bug修复和性能优化。下面我们将深入探讨Java Memcached客户端的一些关键概念和技术细节: 1. **连接管理**:Java Memcached客户端需要建立...
从最初的字节码验证到类加载器的安全机制,以及运行时的访问控制,Java通过一套完整的安全模型确保应用的安全。这套模型支持沙箱概念,限制了未经验证代码的权限,从而在一定程度上阻止了恶意代码的执行。 然而,...
这标志着一种全新的编程语言诞生,它的设计者是被誉为“Java之父”的詹姆斯·格士林博士。 - **设计理念:** Java的设计理念是“一次编写,到处运行”,强调跨平台性,简化程序员的工作负担。 **2. Java版本概述:*...
在Java ME中,通常会有一个名为` MIDlet `的类,它是应用程序的主要组件。 2. **游戏逻辑(Game Logic)**:这部分代码处理拼图游戏的核心算法,包括拼图的生成、旋转、移动以及验证是否完成等操作。可能会有一个...
Java是一门广泛使用的高级编程语言,它由Sun Microsystems公司于1995年发布。由于其“一次编写,到处运行”的跨平台特性,Java迅速成为互联网应用、企业级应用以及移动应用开发的首选语言。本文将从多个方面详细介绍...
- **发展历程**:Java起源于1991年的Green项目,最初命名为Oak(橡树),旨在开发一种新型的智能家电控制系统。然而由于市场环境的变化,Oak并未取得预期的成功。随后,Sun Microsystems将该技术转向了网络应用领域...
1. **数据模型**:OpenTSDB的数据模型基于两个主要元素——指标(Metric)和时间戳(Timestamp)。每个数据点由一个唯一的指标标识,加上一个时间戳和一个或多个度量值(Value)以及可选的标签(Tag)组成。 2. **...
### Java经典入门教程知识点解析 #### 一、Java的基础概念 **1.1 Java作为编程语言** - **语言的本质**:语言本质上是用于交流的工具。对于人类而言,语言如汉语、英语等;对于计算机,则有如Java这样的编程语言...
- **Java的起源**:Java最初是由Sun Microsystems开发的,它的设计理念是为了适应网络化世界的需要。 - **回归小型设备**:尽管Java最初并非专门为小型设备设计,但随着时间的推移,人们逐渐意识到Java对于这类设备...
总的来说,`snappy-java`是一个高效且易于集成的压缩库,对于需要快速处理大量数据的应用场景,如大数据分析、分布式系统和云计算,它是一个理想的选择。开源的特性使得开发者可以直接参与到项目的改进和优化中,...
1. **沙箱模型**:Java的最初设计就包含了安全的概念,它通过沙箱模型限制了代码的执行权限,防止不受信任的代码对系统造成破坏。Java虚拟机(JVM)会执行所有代码之前进行验证,确保它们不会违反安全规则。 2. **...
Java,由Sun Microsystems公司的James Gosling等人在1990年代初期创立,最初被设计用于消费电子设备,如电视转换盒。它源于一个名为"Green"的项目,目标是创建一种小巧、高效且跨平台的编程语言。在设计过程中,开发...