`
倔强的小强
  • 浏览: 42535 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

慢慢琢磨JVM——恭喜JavaEye重新开张

阅读更多

 

1 JVM简介

JVM是我们Javaer的最基本功底了,刚开始学Java的时候,一般都是从“Hello World”开始的,然后会写个复杂点class,然后再找一些开源框架,比如SpringHibernate等等,再然后就开发企业级的应用,比如网站、企业内部应用、实时交易系统等等,直到某一天突然发现做的系统咋就这么慢呢,而且时不时还来个内存溢出什么的,今天是交易系统报了StackOverflowError,明天是网站系统报了个OutOfMemoryError,这种错误又很难重现,只有分析Javacoredump文件,运气好点还能分析出个结果,运行遭的点,就直接去庙里烧香吧!每天接客户的电话都是战战兢兢的,生怕再出什么幺蛾子了。我想Java做的久一点的都有这样的经历,那这些问题的最终根结是在哪呢?—— JVM

JVM全称是Java Virtual MachineJava虚拟机,也就是在计算机上再虚拟一个计算机,这和我们使用 VMWare不一样,那个虚拟的东西你是可以看到的,这个JVM你是看不到的,它存在内存中。我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个JVM也是有这成套的元素,运算器是当然是交给硬件CPU还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM自己的命令集,这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的CPU,比如8086系列的汇编也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集则是可以到处运行的,因为JVM做了翻译,根据不同的CPU,翻译成不同的机器语言。

JVM中我们最需要深入理解的就是它的存储部分,存储?硬盘?NONO JVM是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效,接下来的部分就是重点介绍之。

2 JVM的组成部分

我们先把JVM这个虚拟机画出来,如下图所示:


 

 

从这个图中可以看到,JVM是运行在操作系统之上的,它与硬件没有直接的交互。我们再来看下JVM有哪些组成部分,如下图所示:



 该图参考了网上广为流传的JVM构成图,大家看这个图,整个JVM分为四部分:

q  Class Loader 类加载器

类加载器的作用是加载类文件到内存,比如编写一个HelloWord.java程序,然后通过javac编译成class文件,那怎么才能加载到内存中被执行呢?Class Loader承担的就是这个责任,那不可能随便建立一个.class文件就能被加载的,Class Loader加载的class文件是有格式要求,在《JVM Specification》中式这样定义Class文件的结构:

    ClassFile {

      u4 magic;

      u2 minor_version;

      u2 major_version;

      u2 constant_pool_count;

      cp_info constant_pool[constant_pool_count-1];

      u2 access_flags;

      u2 this_class;

      u2 super_class;

      u2 interfaces_count;

      u2 interfaces[interfaces_count];

      u2 fields_count;

      field_info fields[fields_count];

      u2 methods_count;

      method_info methods[methods_count];

      u2 attributes_count;

      attribute_info attributes[attributes_count];

    }

需要详细了解的话,可以仔细阅读《JVM Specification》的第四章“The class File Format”,这里不再详细说明。

友情提示:Class Loader只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由Execution Engine负责的。

q  Execution Engine 执行引擎

执行引擎也叫做解释器(Interpreter),负责解释命令,提交操作系统执行。

q  Native Interface本地接口

本地接口的作用是融合不同的编程语言为Java所用,它的初衷是融合C/C++程序,Java诞生的时候是C/C++横行的时候,要想立足,必须有一个聪明的、睿智的调用C/C++程序,于是就在内存中专门开辟了一块区域处理标记为native的代码,它的具体做法是Native Method Stack中登记native方法,在Execution Engine执行时加载native libraies。目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过Java程序驱动打印机,或者Java系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用Socket通信,也可以使用Web Service等等,不多做介绍。

q  Runtime data area运行数据区

运行数据区是整个JVM的重点。我们所有写的程序都被加载到这里,之后才开始运行,Java生态系统如此的繁荣,得益于该区域的优良自治,下一章节详细介绍之。

 

整个JVM框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,瞧,一个完整的系统诞生了!

2 JVM的内存管理

所有的数据和程序都是在运行数据区存放,它包括以下几部分:

q  Stack

栈也叫栈内存,是Java程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就Over。问题出来了:栈中存的是那些数据呢?又什么是格式呢?

栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法又调用了B方法,于是产生栈帧F2也被压入栈,执行完毕后,先弹出F2栈帧,再弹出F1栈帧,遵循“先进后出”原则。

那栈帧中到底存在着什么数据呢?栈帧中主要保存3类数据:本地变量(Local Variables),包括输入参数和输出参数以及方法内的变量;栈操作(Operand Stack),记录出栈、入栈的操作;栈帧数据(Frame Data),包括类文件、方法等等。光说比较枯燥,我们画个图来理解一下Java栈,如下图所示:



 图示在一个栈中有两个栈帧,栈帧2是最先被调用的方法,先入栈,然后方法2又调用了方法1,栈帧1处于栈顶的位置,栈帧2处于栈底,执行完毕后,依次弹出栈帧1和栈帧2,线程结束,栈释放。

q  Heap 堆内存

一个JVM实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:

Permanent Space 永久存储区

永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。

Young Generation Space 新生区

新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1区也满了呢?再移动到养老区。

Tenure generation space养老区

养老区用于保存从新生区筛选出来的JAVA对象,一般池对象都在这个区域活跃。   三个区的示意图如下:



 q  Method Area 方法区

方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。

q  PC Register 程序计数器

每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎读取下一条指令。

q  Native Method Stack 本地方法栈

 

 

 

3 JVM相关问题

问:堆和栈有什么区别

答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的。

栈是跟随线程的,有线程就有栈,堆是跟随JVM的,有JVM就有堆内存。

 

问:堆内存中到底存在着什么东西?

答:对象,包括对象变量以及对象方法。

 

问:类变量和实例变量有什么区别?

答:静态变量是类变量,非静态变量是实例变量,直白的说,有static修饰的变量是静态变量,没有static修饰的变量是实例变量。静态变量存在方法区中,实例变量存在堆内存中。

 

问:我听说类变量是在JVM启动时就初始化好的,和你这说的不同呀!

答:那你是道听途说,信我的,没错。

 

问:Java的方法(函数)到底是传值还是传址?

答:都不是,是以传值的方式传递地址,具体的说原生数据类型传递的值,引用类型传递的地址。对于原始数据类型,JVM的处理方法是从Method AreaHeap中拷贝到Stack,然后运行frame中的方法,运行完毕后再把变量指拷贝回去。

 

问:为什么会产生OutOfMemory产生?

答:一句话:Heap内存中没有足够的可用内存了。这句话要好好理解,不是说Heap没有内存了,是说新申请内存的对象大于Heap空闲内存,比如现在Heap还空闲1M,但是新申请的内存需要1.1M,于是就会报OutOfMemory了,可能以后的对象申请的内存都只要0.9M,于是就只出现一次OutOfMemoryGC也正常了,看起来像偶发事件,就是这么回事。       但如果此时GC没有回收就会产生挂起情况,系统不响应了。

 

问:我产生的对象不多呀,为什么还会产生OutOfMemory

答:你继承层次忒多了,Heap 产生的对象是先产生 父类,然后才产生子类,明白不?

 

问:OutOfMemory错误分几种?

答:分两种,分别是“OutOfMemoryError:java heap sizeOutOfMemoryError: PermGen space,两种都是内存溢出,heap size是说申请不到新的内存了,这个很常见,检查应用或调整堆内存大小。

PermGen space是因为永久存储区满了,这个也很常见,一般在热发布的环境中出现,是因为每次发布应用系统都不重启,久而久之永久存储区中的死对象太多导致新对象无法申请内存,一般重新启动一下即可。

 

问:为什么会产生StackOverflowError

答:因为一个线程把Stack内存全部耗尽了,一般是递归函数造成的。

 

问:一个机器上可以看多个JVM吗?JVM之间可以互访吗?

答:可以多个JVM,只要机器承受得了。JVM之间是不可以互访,你不能在A-JVM中访问B-JVMHeap内存,这是不可能的。在以前老版本的JVM中,会出现A-JVM Crack后影响到B-JVM,现在版本非常少见。

 

问:为什么Java要采用垃圾回收机制,而不采用C/C++的显式内存管理?

答:为了简单,内存管理不是每个程序员都能折腾好的。

 

问:为什么你没有详细介绍垃圾回收机制?

答:垃圾回收机制每个JVM都不同,JVM Specification只是定义了要自动释放内存,也就是说它只定义了垃圾回收的抽象方法,具体怎么实现各个厂商都不同,算法各异,这东西实在没必要深入。

 

问:JVM中到底哪些区域是共享的?哪些是私有的?

答:HeapMethod Area是共享的,其他都是私有的,

 

问:什么是JIT,你怎么没说?

答:JIT是指Just In Time,有的文档把JIT作为JVM的一个部件来介绍,有的是作为执行引擎的一部分来介绍,这都能理解。Java刚诞生的时候是一个解释性语言,别嘘,即使编译成了字节码(byte code)也是针对JVM的,它需要再次翻译成原生代码(native code)才能被机器执行,于是效率的担忧就提出来了。Sun为了解决该问题提出了一套新的机制,好,你想编译成原生代码,没问题,我在JVM上提供一个工具,把字节码编译成原生码,下次你来访问的时候直接访问原生码就成了,于是JIT就诞生了,就这么回事。

 

问:JVM还有哪些部分是你没有提到的?

答:JVM是一个异常复杂的东西,写一本砖头书都不为过,还有几个要说明的:

常量池(constant pool):按照顺序存放程序中的常量,并且进行索引编号的区域。比如int i =100,这个100就放在常量池中。

安全管理器(Security Manager):提供Java运行期的安全控制,防止恶意攻击,比如指定读取文件,写入文件权限,网络访问,创建进程等等,Class LoaderSecurity Manager认证通过后才能加载class文件的。

方法索引表(Methods table),记录的是每个method的地址信息,StackHeap中的地址指针其实是指向Methods table地址。

      

问:为什么不建议在程序中显式的生命System.gc()

答:因为显式声明是做堆内存全扫描,也就是Full GC,是需要停止所有的活动的(Stop  The World Collection),你的应用能承受这个吗?

 

问:JVM有哪些调整参数?

答:非常多,自己去找,堆内存、栈内存的大小都可以定义,甚至是堆内存的三个部分、新生代的各个比例都能调整。

 

拷贝过来后格式都变了,就附上PDF文件。

欢迎重拍,别手下留情,我喜欢~~~~

  • 大小: 13.4 KB
  • 大小: 19.8 KB
  • 大小: 34.9 KB
  • 大小: 50.5 KB
分享到:
评论
48 楼 Cupenoruler 2014-03-07  
写的不错,点个赞~
47 楼 hairongtian 2012-09-09  
讲的不错,顶!
46 楼 aspnetdb 2011-04-15  
开心,今天看了好多不错的文章。
45 楼 coolcat9527 2011-02-23  
谢谢LZ分享 好多东西都没仔细的去琢磨
44 楼 liuxuejin 2011-02-11  
lz能不能结合进程说说JVM啊?哪个进程是虚拟机的进程?怎么监视整个进程,找出性能瓶颈
43 楼 寻找我的ID 2011-02-11  
不错,挺系统。
在PDF中“在栈内存中有三个栈帧,如下图所示”,这里感觉不太对,函数执行完,对应的栈帧就出栈了,所以不会三个栈帧同时存在,即runStaticMethod和runNonStaticMethod不会同时存在栈中。
42 楼 husan 2011-02-10  
不错,解决了以前的一点疑问
41 楼 Dxx23 2011-01-28  
下载研究了!
40 楼 cectsky 2010-12-14  
一说到延迟加载,就想到hibernate,最近有点压力哈
39 楼 umeit 2010-12-14  
taolei0628 写道
文章比较中肯,也比较全面,至于某些说的不是太准确的地方,应该是为了方便入门吧。
那个i=100的,100可不是放在常量池里。jvm里short数值范围内的整数常量是有专门指令的(bipush),short范围之外的才会放在常量池里。
那个如何解决OutofMemory的问题,当然是要分析你是怎么把内存(或其他资源)用光的。
那个类何时初始化的问题,楼主说的没错,jvm是延迟加载的,你用到时才加载,你执行时才初始化。


请教:

“用到时才加载”是什么意思?能否举例说一下?
还有,
“jvm是延迟加载的”是始终都是延迟加载吗?是否有其他的加载方式?
38 楼 novoland 2010-12-09  
"Permanent Space 永久存储区"貌似只是SUN HOTSPOT JVM对JVM规范中Method Area的一种实现,这里把这部分归为heap的一部分,应该是不正确的吧
37 楼 Chen.H 2010-11-29  
<div class="quote_title">倔强的小强 写道</div>
<div class="quote_div">
<p> </p>
<h2>
<span lang="EN-US">1 JVM</span><span>简介</span> </h2>
<p class="MsoNormal"><span lang="EN-US">JVM</span><span>是我们<span lang="EN-US">Javaer</span>的最基本功底了,刚开始学<span lang="EN-US">Java</span>的时候,一般都是从“<span lang="EN-US">Hello World</span>”开始的,然后会写个复杂点<span lang="EN-US">class</span>,然后再找一些开源框架,比如<span lang="EN-US">Spring</span>,<span lang="EN-US">Hibernate</span>等等,再然后就开发企业级的应用,比如网站、企业内部应用、实时交易系统等等,直到某一天突然发现做的系统咋就这么慢呢,而且时不时还来个内存溢出什么的,今天是交易系统报了<span lang="EN-US">StackOverflowError</span>,明天是网站系统报了个<span lang="EN-US">OutOfMemoryError</span>,这种错误又很难重现,只有分析<span lang="EN-US">Javacore</span>和<span lang="EN-US">dump</span>文件,运气好点还能分析出个结果,运行遭的点,就直接去庙里烧香吧!每天接客户的电话都是战战兢兢的,生怕再出什么幺蛾子了。我想<span lang="EN-US">Java</span>做的久一点的都有这样的经历,那这些问题的最终根结是在哪呢?——<span lang="EN-US"> JVM</span>。</span></p>
<p class="MsoNormal"><span lang="EN-US">JVM</span><span>全称是<span lang="EN-US">Java Virtual Machine</span>,<span lang="EN-US">Java</span>虚拟机,也就是在计算机上再虚拟一个计算机,这和我们使用<span lang="EN-US"> VMWare</span>不一样,那个虚拟的东西你是可以看到的,这个<span lang="EN-US">JVM</span>你是看不到的,它存在内存中。我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个<span lang="EN-US">JVM</span>也是有这成套的元素,运算器是当然是交给硬件<span lang="EN-US">CPU</span>还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了<span lang="EN-US">JVM</span>自己的命令集,这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的<span lang="EN-US">CPU</span>,比如<span lang="EN-US">8086</span>系列的汇编也是可以用在<span lang="EN-US">8088</span>上的,但是就不能跑在<span lang="EN-US">8051</span>上,而<span lang="EN-US">JVM</span>的命令集则是可以到处运行的,因为<span lang="EN-US">JVM</span>做了翻译,根据不同的<span lang="EN-US">CPU</span>,翻译成不同的机器语言。</span></p>
<p class="MsoNormal"><span lang="EN-US">JVM</span><span>中我们最需要深入理解的就是它的存储部分,存储?硬盘?<span lang="EN-US">NO</span>,<span lang="EN-US">NO</span>,<span lang="EN-US"> JVM</span>是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中,这决定着我们程序运行的是否健壮、是否高效,接下来的部分就是重点介绍之。</span></p>
<h2>
<span lang="EN-US">2 JVM</span><span>的组成部分</span> </h2>
<p class="MsoNormal"><span>我们先把<span lang="EN-US">JVM</span>这个虚拟机画出来,如下图所示:</span></p>
<p><br><img src="http://dl.iteye.com/upload/attachment/353109/0c3fda2c-286b-3819-b47a-4daf15d3d08c.png" alt=""></p>
<p> </p>
<p> </p>
<p class="MsoNormal"><span>从这个图中可以看到,<span lang="EN-US">JVM</span>是运行在操作系统之上的,它与硬件没有直接的交互。我们再来看下<span lang="EN-US">JVM</span>有哪些组成部分,如下图所示:</span></p>
<p class="MsoNormal"><br><img src="http://dl.iteye.com/upload/attachment/353113/a3a81279-4006-3fa6-9b7f-e1ac95228efd.png" alt=""><br> <span>该图参考了网上广为流传的<span lang="EN-US">JVM</span>构成图,大家看这个图,整个<span lang="EN-US">JVM</span>分为四部分:</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Class Loader </span><span>类加载器</span></p>
<p class="MsoNormal"><span>类加载器的作用是加载类文件到内存,比如编写一个<span lang="EN-US">HelloWord.java</span>程序,然后通过<span lang="EN-US">javac</span>编译成<span lang="EN-US">class</span>文件,那怎么才能加载到内存中被执行呢?<span lang="EN-US">Class Loader</span>承担的就是这个责任,那不可能随便建立一个<span lang="EN-US">.class</span>文件就能被加载的,<span lang="EN-US">Class Loader</span>加载的<span lang="EN-US">class</span>文件是有格式要求,在《<span lang="EN-US">JVM Specification</span>》中式这样定义<span lang="EN-US">Class</span>文件的结构:</span></p>
<div style="padding: 4.0pt 4.0pt 4.0pt 4.0pt; margin-left: 0cm; margin-right: 28.65pt;">
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span>ClassFile {</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u4 magic;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 minor_version;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span> </span><span>   </span><span>  </span>u2 major_version;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 constant_pool_count;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>cp_info constant_pool[constant_pool_count-1];</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 access_flags;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 this_class;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 super_class;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 interfaces_count;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 interfaces[interfaces_count];</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 fields_count;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>field_info fields[fields_count];</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 methods_count;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>method_info methods[methods_count];</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>u2 attributes_count;</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span><span>  </span>attribute_info attributes[attributes_count];</span></p>
<p class="a" style="margin-right: 0cm; text-indent: 17.5pt;"><span style="color: windowtext; font-size: 10pt;"><span>    </span>}</span></p>
</div>
<p class="MsoNormal"><span>需要详细了解的话,可以仔细阅读《<span lang="EN-US">JVM Specification</span>》的第四章“<span lang="EN-US">The class File Format</span>”,这里不再详细说明。</span></p>
<p class="MsoNormal"><span>友情提示:<span lang="EN-US">Class Loader</span>只管加载,只要符合文件结构就加载,至于说能不能运行,则不是它负责的,那是由<span lang="EN-US">Execution Engine</span>负责的。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Execution Engine </span><span>执行引擎</span></p>
<p class="MsoNormal"><span>执行引擎也叫做解释器<span lang="EN-US">(Interpreter)</span>,负责解释命令,提交操作系统执行。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Native Interface</span><span>本地接口</span></p>
<p class="MsoNormal"><span>本地接口的作用是融合不同的编程语言为<span lang="EN-US">Java</span>所用,它的初衷是融合<span lang="EN-US">C/C++</span>程序,<span lang="EN-US">Java</span>诞生的时候是<span lang="EN-US">C/C++</span>横行的时候,要想立足,必须有一个聪明的、睿智的调用<span lang="EN-US">C/C++</span>程序,于是就在内存中专门开辟了一块区域处理标记为<span lang="EN-US">native</span>的代码,它的具体做法是<span lang="EN-US">Native Method Stack</span>中登记<span lang="EN-US">native</span>方法,在<span lang="EN-US">Execution Engine</span>执行时加载<span lang="EN-US">native libraies</span>。目前该方法使用的是越来越少了,除非是与硬件有关的应用,比如通过<span lang="EN-US">Java</span>程序驱动打印机,或者<span lang="EN-US">Java</span>系统管理生产设备,在企业级应用中已经比较少见,因为现在的异构领域间的通信很发达,比如可以使用<span lang="EN-US">Socket</span>通信,也可以使用<span lang="EN-US">Web Service</span>等等,不多做介绍。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Runtime data area</span><span>运行数据区</span></p>
<p class="MsoNormal"><span>运行数据区是整个<span lang="EN-US">JVM</span>的重点。我们所有写的程序都被加载到这里,之后才开始运行,<span lang="EN-US">Java</span>生态系统如此的繁荣,得益于该区域的优良自治,下一章节详细介绍之。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal"><span>整个<span lang="EN-US">JVM</span>框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,瞧,一个完整的系统诞生了!</span></p>
<h2>
<span lang="EN-US">2 JVM</span><span>的内存管理</span> </h2>
<p class="MsoNormal"><span>所有的数据和程序都是在运行数据区存放,它包括以下几部分:</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Stack </span><span>栈</span></p>
<p class="MsoNormal"><span>栈也叫栈内存,是<span lang="EN-US">Java</span>程序的运行区,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束,该栈就<span lang="EN-US">Over</span>。问题出来了:栈中存的是那些数据呢?又什么是格式呢?</span></p>
<p class="MsoNormal"><span>栈中的数据都是以栈帧(</span><span lang="EN-US">Stack Frame</span><span>)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法</span><span lang="EN-US">(Method)</span><span>和运行期数据的数据集,当一个方法</span><span lang="EN-US">A</span><span>被调用时就产生了一个栈帧</span><span lang="EN-US">F1</span><span>,并被压入到栈中,</span><span lang="EN-US">A</span><span>方法又调用了</span><span lang="EN-US">B</span><span>方法,于是产生栈帧</span><span lang="EN-US">F2</span><span>也被压入栈,执行完毕后,先弹出</span><span lang="EN-US">F2</span><span>栈帧,再弹出</span><span lang="EN-US">F1</span><span>栈帧,遵循“先进后出”原则。</span></p>
<p class="MsoNormal"><span>那栈帧中到底存在着什么数据呢?栈帧中主要保存<span lang="EN-US">3</span>类数据:本地变量(<span lang="EN-US">Local Variables</span>),包括输入参数和输出参数以及方法内的变量;栈操作(<span lang="EN-US">Operand Stack</span>),记录出栈、入栈的操作;栈帧数据(<span lang="EN-US">Frame Data</span>),包括类文件、方法等等。光说比较枯燥,我们画个图来理解一下<span lang="EN-US">Java</span>栈,如下图所示:</span></p>
<p class="MsoNormal"><br><img src="http://dl.iteye.com/upload/attachment/353115/183617ef-4489-3b4d-a3f0-ec628cd7241f.png" alt=""><br> <span>图示在一个栈中有两个栈帧,栈帧<span lang="EN-US">2</span>是最先被调用的方法,先入栈,然后方法<span lang="EN-US">2</span>又调用了方法<span lang="EN-US">1</span>,栈帧<span lang="EN-US">1</span>处于栈顶的位置,栈帧<span lang="EN-US">2</span>处于栈底,执行完毕后,依次弹出栈帧<span lang="EN-US">1</span>和栈帧<span lang="EN-US">2</span>,线程结束,栈释放。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US"></span><span lang="EN-US">Heap </span><span>堆内存</span></p>
<p class="MsoNormal"><span>一个<span lang="EN-US">JVM</span>实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:</span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span lang="EN-US">Permanent Space </span></strong><strong><span>永久存储区</span></strong></p>
<p class="MsoNormal"><span>永久存储区是一个常驻内存区域,用于存放<span lang="EN-US">JDK</span>自身所携带的<span lang="EN-US">Class,Interface</span>的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭<span lang="EN-US">JVM</span>才会释放此区域所占用的内存。</span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span lang="EN-US">Young Generation Space </span></strong><strong><span>新生区</span></strong></p>
<p class="MsoNormal"><span>新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分:伊甸区(<span lang="EN-US">Eden space</span>)和幸存者区(<span lang="EN-US">Survivor pace</span>),所有的类都是在伊甸区被<span lang="EN-US">new</span>出来的。幸存区有两个:<span lang="EN-US"> 0</span>区(<span lang="EN-US">Survivor 0 space</span>)和<span lang="EN-US">1</span>区(<span lang="EN-US">Survivor 1 space</span>)。当伊甸园的空间用完时,程序又需要创建对象,<span lang="EN-US">JVM</span>的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存<span lang="EN-US">0</span>区。若幸存<span lang="EN-US">0</span>区也满了,再对该区进行垃圾回收,然后移动到<span lang="EN-US">1</span>区。那如果<span lang="EN-US">1</span>区也满了呢?再移动到养老区。</span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span lang="EN-US">Tenure generation space</span></strong><strong><span>养老区</span></strong></p>
<p class="MsoNormal"><span>养老区用于保存从新生区筛选出来的<span lang="EN-US">JAVA</span>对象,一般池对象都在这个区域活跃。<span lang="EN-US"><span>   </span></span>三个区的示意图如下:</span></p>
<p class="MsoNormal"><br><img src="http://dl.iteye.com/upload/attachment/353117/e1476bc8-85c2-36ba-9e47-77d34361f097.jpg" alt=""><br> <span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Method Area </span><span>方法区</span></p>
<p class="MsoNormal"><span>方法区是被所有线程共享,该区域保存所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">PC Register </span><span>程序计数器</span></p>
<p class="MsoNormal"><span>每个线程都有一个程序计数器,就是一个指针,指向方法区中的方法字节码,由执行引擎读取下一条指令。</span></p>
<p class="MsoNormal"><span style="font-family: Wingdings;"><span>q<span style="font: 7.0pt ;">  </span></span></span><span lang="EN-US">Native Method Stack </span><span>本地方法栈</span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<h2>
<span lang="EN-US">3 JVM</span><span>相关问题</span> </h2>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:堆和栈有什么区别</span></strong></p>
<p class="MsoNormal"><span>答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的</span><span lang="EN-US">methodVar</span><span>是在运行期存放到栈中的。</span></p>
<p class="MsoNormal"><span>栈是跟随线程的,有线程就有栈,堆是跟随</span><span lang="EN-US">JVM</span><span>的,有</span><span lang="EN-US">JVM</span><span>就有堆内存。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:堆内存中到底存在着什么东西?</span></strong></p>
<p class="MsoNormal"><span>答:对象,包括对象变量以及对象方法。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:类变量和实例变量有什么区别?</span></strong></p>
<p class="MsoNormal"><span>答:静态变量是类变量,非静态变量是实例变量,直白的说,有</span><span lang="EN-US">static</span><span>修饰的变量是静态变量,没有</span><span lang="EN-US">static</span><span>修饰的变量是实例变量。静态变量存在方法区中,实例变量存在堆内存中。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:我听说类变量是在</span><span lang="EN-US">JVM</span></strong><strong><span>启动时就初始化好的,和你这说的不同呀!</span></strong></p>
<p class="MsoNormal"><span>答:那你是道听途说,信我的,没错。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:</span><span lang="EN-US">Java</span></strong><strong><span>的方法(函数)到底是传值还是传址?</span></strong></p>
<p class="MsoNormal"><span>答:都不是,是以传值的方式传递地址,具体的说原生数据类型传递的值,引用类型传递的地址。对于原始数据类型,</span><span lang="EN-US">JVM</span><span>的处理方法是从</span><span lang="EN-US">Method Area</span><span>或</span><span lang="EN-US">Heap</span><span>中拷贝到</span><span lang="EN-US">Stack</span><span>,然后运行</span><span lang="EN-US">frame</span><span>中的方法,运行完毕后再把变量指拷贝回去。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:为什么会产生</span><span lang="EN-US">OutOfMemory</span></strong><strong><span>产生?</span></strong></p>
<p class="MsoNormal"><span>答:一句话:</span><span lang="EN-US">Heap</span><span>内存中没有足够的可用内存了。这句话要好好理解,不是说</span><span lang="EN-US">Heap</span><span>没有内存了,是说新申请内存的对象大于</span><span lang="EN-US">Heap</span><span>空闲内存,比如现在</span><span lang="EN-US">Heap</span><span>还空闲</span><span lang="EN-US">1M</span><span>,但是新申请的内存需要</span><span lang="EN-US">1.1M</span><span>,于是就会报</span><span lang="EN-US">OutOfMemory</span><span>了,可能以后的对象申请的内存都只要</span><span lang="EN-US">0.9M</span><span>,于是就只出现一次</span><span lang="EN-US">OutOfMemory</span><span>,</span><span lang="EN-US">GC</span><span>也正常了,看起来像偶发事件,就是这么回事。</span><span lang="EN-US"><span>       </span></span><span>但如果此时</span><span lang="EN-US">GC</span><span>没有回收就会产生挂起情况,系统不响应了。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:我产生的对象不多呀,为什么还会产生</span><span lang="EN-US">OutOfMemory</span></strong><strong><span>?</span></strong></p>
<p class="MsoNormal"><span>答:你继承层次忒多了,</span><span lang="EN-US">Heap</span><span>中</span> <span>产生的对象是先产生</span> <span>父类,然后才产生子类,明白不?</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:</span><span lang="EN-US">OutOfMemory</span></strong><strong><span>错误分几种?</span></strong></p>
<p class="MsoNormal"><span>答:分两种,分别是“</span><span lang="EN-US">OutOfMemoryError:java heap size</span><span lang="EN-US">”</span><span>和<span lang="EN-US">”</span></span><span lang="EN-US">OutOfMemoryError: PermGen space</span><span lang="EN-US">”</span><span>,两种都是内存溢出,</span><span lang="EN-US">heap size</span><span>是说申请不到新的内存了,这个很常见,检查应用或调整堆内存大小。</span></p>
<p class="MsoNormal"><span>“</span><span lang="EN-US">PermGen space</span><span lang="EN-US">”</span><span>是因为永久存储区满了,这个也很常见,一般在热发布的环境中出现,是因为每次发布应用系统都不重启,久而久之永久存储区中的死对象太多导致新对象无法申请内存,一般重新启动一下即可。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:为什么会产生</span><span lang="EN-US">StackOverflowError</span></strong><strong><span>?</span></strong></p>
<p class="MsoNormal"><span>答:因为一个线程把</span><span lang="EN-US">Stack</span><span>内存全部耗尽了,一般是递归函数造成的。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:一个机器上可以看多个</span><span lang="EN-US">JVM</span></strong><strong><span>吗?</span><span lang="EN-US">JVM</span></strong><strong><span>之间可以互访吗?</span></strong></p>
<p class="MsoNormal"><span>答:可以多个</span><span lang="EN-US">JVM</span><span>,只要机器承受得了。</span><span lang="EN-US">JVM</span><span>之间是不可以互访,你不能在</span><span lang="EN-US">A-JVM</span><span>中访问</span><span lang="EN-US">B-JVM</span><span>的</span><span lang="EN-US">Heap</span><span>内存,这是不可能的。在以前老版本的</span><span lang="EN-US">JVM</span><span>中,会出现</span><span lang="EN-US">A-JVM Crack</span><span>后影响到</span><span lang="EN-US">B-JVM</span><span>,现在版本非常少见。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:为什么</span><span lang="EN-US">Java</span></strong><strong><span>要采用垃圾回收机制,而不采用</span><span lang="EN-US">C/C++</span></strong><strong><span>的显式内存管理?</span></strong></p>
<p class="MsoNormal"><span>答:为了简单,内存管理不是每个程序员都能折腾好的。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:为什么你没有详细介绍垃圾回收机制?</span></strong></p>
<p class="MsoNormal"><span>答:垃圾回收机制每个</span><span lang="EN-US">JVM</span><span>都不同,</span><span lang="EN-US">JVM Specification</span><span>只是定义了要自动释放内存,也就是说它只定义了垃圾回收的抽象方法,具体怎么实现各个厂商都不同,算法各异,这东西实在没必要深入。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:</span><span lang="EN-US">JVM</span></strong><strong><span>中到底哪些区域是共享的?哪些是私有的?</span></strong></p>
<p class="MsoNormal"><span>答:</span><span lang="EN-US">Heap</span><span>和</span><span lang="EN-US">Method Area</span><span>是共享的,其他都是私有的,</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:什么是</span><span lang="EN-US">JIT</span></strong><strong><span>,你怎么没说?</span></strong></p>
<p class="MsoNormal"><span>答:</span><span lang="EN-US">JIT</span><span>是指</span><span lang="EN-US">Just In Time</span><span>,有的文档把</span><span lang="EN-US">JIT</span><span>作为</span><span lang="EN-US">JVM</span><span>的一个部件来介绍,有的是作为执行引擎的一部分来介绍,这都能理解。</span><span lang="EN-US">Java</span><span>刚诞生的时候是一个解释性语言,别嘘,即使编译成了字节码(</span><span lang="EN-US">byte code</span><span>)也是针对</span><span lang="EN-US">JVM</span><span>的,它需要再次翻译成原生代码</span><span lang="EN-US">(native code)</span><span>才能被机器执行,于是效率的担忧就提出来了。</span><span lang="EN-US">Sun</span><span>为了解决该问题提出了一套新的机制,好,你想编译成原生代码,没问题,我在</span><span lang="EN-US">JVM</span><span>上提供一个工具,把字节码编译成原生码,下次你来访问的时候直接访问原生码就成了,于是</span><span lang="EN-US">JIT</span><span>就诞生了,就这么回事。</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:</span><span lang="EN-US">JVM</span></strong><strong><span>还有哪些部分是你没有提到的?</span></strong></p>
<p class="MsoNormal"><span>答:</span><span lang="EN-US">JVM</span><span>是一个异常复杂的东西,写一本砖头书都不为过,还有几个要说明的:</span></p>
<p class="MsoNormal"><span>常量池(</span><span lang="EN-US">constant pool</span><span>):按照顺序存放程序中的常量,并且进行索引编号的区域。比如</span><span lang="EN-US">int i =100</span><span>,这个</span><span lang="EN-US">100</span><span>就放在常量池中。</span></p>
<p class="MsoNormal"><span>安全管理器(</span><span lang="EN-US">Security Manager</span><span>):提供</span><span lang="EN-US">Java</span><span>运行期的安全控制,防止恶意攻击,比如指定读取文件,写入文件权限,网络访问,创建进程等等,</span><span lang="EN-US">Class Loader</span><span>在</span><span lang="EN-US">Security Manager</span><span>认证通过后才能加载</span><span lang="EN-US">class</span><span>文件的。</span></p>
<p class="MsoNormal"><span>方法索引表(</span><span lang="EN-US">Methods table</span><span>),记录的是每个</span><span lang="EN-US">method</span><span>的地址信息,</span><span lang="EN-US">Stack</span><span>和</span><span lang="EN-US">Heap</span><span>中的地址指针其实是指向</span><span lang="EN-US">Methods table</span><span>地址。</span></p>
<p class="MsoNormal" style="text-indent: 0cm;"><span lang="EN-US"><span>       </span></span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:为什么不建议在程序中显式的生命</span><span lang="EN-US">System.gc()</span></strong><strong><span>?</span></strong></p>
<p class="MsoNormal"><span>答:因为显式声明是做堆内存全扫描,也就是</span><span lang="EN-US">Full GC</span><span>,是需要停止所有的活动的(</span><span lang="EN-US">Stop <span> </span>The World Collection</span><span>),你的应用能承受这个吗?</span></p>
<p class="MsoNormal"><span lang="EN-US"> </span></p>
<p class="MsoNormal" style="text-indent: 21.1pt;"><strong><span>问:</span><span lang="EN-US">JVM</span></strong><strong><span>有哪些调整参数?</span></strong></p>
<p class="MsoNormal"><span>答:非常多,自己去找,堆内存、栈内存的大小都可以定义,甚至是堆内存的三个部分、新生代的各个比例都能调整。</span></p>
<p class="MsoNormal"> </p>
<p class="MsoNormal"><span style="color: #ff0000;"><strong><span style="font-size: x-large;"><span style="font-size: x-large;"><span style="color: #ff0000;"><span>拷贝过来后格式都变了,就附上PDF文件。</span></span></span></span></strong></span></p>
<p class="MsoNormal"><span style="font-size: x-large;"><strong><span style="color: #ff0000;"><span>欢迎重拍,别手下留情,我喜欢~~~~</span></span></strong></span></p>
</div>
<p> </p>
36 楼 bugmenot 2010-11-29  
楼主大大的第一张图把SPARC写成SPAC了。
而且一般JVM与操作系统、硬件间的关系未必是那张图所表示的,而应该像下图一样,在②、③、⑦处都与底层有接触

(引用自《虚拟机:系统与进程的通用平台》第一章)

倔强的小强 写道
问:堆和栈有什么区别

答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的。

栈是跟随线程的,有线程就有栈,堆是跟随JVM的,有JVM就有堆内存。

大大请教对象内的临时变量是什么来的?

倔强的小强 写道
问:Java的方法(函数)到底是传值还是传址?

答:都不是,是以传值的方式传递地址,具体的说原生数据类型传递的值,引用类型传递的地址。对于原始数据类型,JVM的处理方法是从Method Area或Heap中拷贝到Stack,然后运行frame中的方法,运行完毕后再把变量指拷贝回去。

大大请教红色部分是什么意思?能用图解释一下?

倔强的小强 写道
问:为什么你没有详细介绍垃圾回收机制?

答:垃圾回收机制每个JVM都不同,JVM Specification只是定义了要自动释放内存,也就是说它只定义了垃圾回收的抽象方法,具体怎么实现各个厂商都不同,算法各异,这东西实在没必要深入。

但是JVM Specification同样没有定义Java堆的构成方式,也就是说并不规定Java堆要划分为永久存储区、新生区、养老区。
倔强的小强 写道
Heap 堆内存

一个JVM实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行,堆内存分为三部分:

Permanent Space 永久存储区

永久存储区是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭JVM才会释放此区域所占用的内存。

Young Generation Space 新生区

新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被new出来的。幸存区有两个: 0区(Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存0区。若幸存0区也满了,再对该区进行垃圾回收,然后移动到1区。那如果1区也满了呢?再移动到养老区。

Tenure generation space养老区

养老区用于保存从新生区筛选出来的JAVA对象,一般池对象都在这个区域活跃。

大大可能已经知道,所谓永久存储区是用来实现JVM Specification的方法区的吧。

倔强的小强 写道
问:JVM还有哪些部分是你没有提到的?

答:JVM是一个异常复杂的东西,写一本砖头书都不为过,还有几个要说明的:

常量池(constant pool):按照顺序存放程序中的常量,并且进行索引编号的区域。比如int i =100,这个100就放在常量池中。

安全管理器(Security Manager):提供Java运行期的安全控制,防止恶意攻击,比如指定读取文件,写入文件权限,网络访问,创建进程等等,Class Loader在Security Manager认证通过后才能加载class文件的。

方法索引表(Methods table),记录的是每个method的地址信息,Stack和Heap中的地址指针其实是指向Methods table地址。

大大请教红色部分是什么意思?

倔强的小强 写道
问:为什么不建议在程序中显式的生命System.gc()?

答:因为显式声明是做堆内存全扫描,也就是Full GC,是需要停止所有的活动的(Stop  The World Collection),你的应用能承受这个吗?

大大请看JRockit的文档
引用
This method can act differently on the JRockit JVM than on other JVMs. On the JRockit JVM, calling the System.gc() method results in a nursery collection if you are using a generational collector, and an old space collection if you are using a single generational collector.

JVM Specification同样没有规定System.gc()要做Full GC,实现选择做minor GC或者完全忽略它(-XX:+DisableExplicitGC)都完全合理。
35 楼 woniu1983 2010-11-29  
分离的北极熊 写道
问:我听说类变量是在JVM启动时就初始化好的,和你这说的不同呀!
答:那你是道听途说,信我的,没错。

class test{
static int i;

……main(String[] args){
  sysout(i);
}
}
难道它会报错?



JVM启动的时候怎么可能知道有哪些类,哪个类中有哪些变量呢?

所以说JVM启动时就初始化某个类变量是不可能的。

在JVM启动之后,在执行main方法时(这里会有一个main线程),首先会去初始化test类以及该类的static field 包括static block。这个时候才是类变量的初始化。

我认为是这样的
34 楼 taolei0628 2010-11-26  
文章比较中肯,也比较全面,至于某些说的不是太准确的地方,应该是为了方便入门吧。
那个i=100的,100可不是放在常量池里。jvm里short数值范围内的整数常量是有专门指令的(bipush),short范围之外的才会放在常量池里。
那个如何解决OutofMemory的问题,当然是要分析你是怎么把内存(或其他资源)用光的。
那个类何时初始化的问题,楼主说的没错,jvm是延迟加载的,你用到时才加载,你执行时才初始化。
33 楼 scholers 2010-11-26  
写的很好啊!
32 楼 jssay 2010-11-26  
总结的不错,表达的也很清楚,期待lz新作
31 楼 linux1689 2010-11-26  
adairfly 写道
学习了,收获不小,但不知是转帖还是原创


绝对是原创!

不过,无论是转帖还是原创,你们不都受益了么?
30 楼 adairfly 2010-11-26  
学习了,收获不小,但不知是转帖还是原创
29 楼 zjugzg 2010-11-26  
赞 养老区,一直不知道该怎么用中文称呼这个区

相关推荐

    JVM——总结思维导图

    JVM——总结思维导图

    学习笔记

    2. **慢慢琢磨JVM——恭喜JavaEye重新开张** JVM(Java虚拟机)是Java程序运行的基础,它负责解析字节码并执行。理解JVM的工作原理,包括内存模型、类加载机制、垃圾回收等,对于优化Java应用程序性能至关重要。 3...

    慢慢琢磨jvm 经典

    ### 深入解析JVM:Java虚拟机的精髓与挑战 #### JVM概览与重要性 JVM,即Java Virtual Machine(Java虚拟机),是Java程序员必须掌握的核心技术之一。初学者通常从简单的“HelloWorld”程序开始,逐渐接触更复杂的...

    JVM ——word文档总结下载地址-附件资源

    JVM ——word文档总结下载地址-附件资源

    JVM——Java虚拟机架构

    JVM=类加载器classloader+执行引擎executionengine+运行时数据区域runtimedataarea首先Java源代码文件被Java编译器编译为字节码文件,然后JVM中的类加载器加载完毕之后,交由JVM执行引擎执行。在整个

    实战Java虚拟机——JVM故障诊断与性能优化 pdf

    《实战Java虚拟机——JVM故障诊断与性能优化》内容简介:随着越来越多的第三方语言(Groovy、Scala、JRuby等)在Java虚拟机上运行,Java也俨然成为一个充满活力的生态圈。本书将通过200余示例详细介绍Java虚拟机中的...

    实战Java虚拟机——JVM故障诊断与性能优化.pdf

    《实战Java虚拟机——JVM故障诊断与性能优化》是一本深入探讨Java开发中的关键环节——Java虚拟机(JVM)的专著。本书聚焦于实际应用中的问题解决和性能调优,对于Java开发者和系统管理员来说,是提升技术水平的重要...

    实战Java虚拟机——JVM故障诊断与性能优化

    《实战Java虚拟机——JVM故障诊断与性能优化》是一本深入探讨Java开发人员和运维人员必备技能的书籍。本书作者葛一鸣以其丰富的实战经验,详细阐述了JVM(Java Virtual Machine)的工作原理,以及如何有效地进行故障...

    java未来发展——JVM7

    【Java未来发展——JVM7】 Java作为一门广泛使用的编程语言,其未来发展一直是开发者关注的焦点。JVM(Java虚拟机)作为Java平台的核心,它的每一次重大更新都会对Java生态系统产生深远影响。JVM7,即Java 7,是...

    jvmjava,java实现的JVM。.zip

    《Java实现的JVM——深入理解与实践》 在计算机科学领域,Java虚拟机(JVM)是Java语言的关键组成部分,它使得Java程序能够在不同平台上运行而无需重新编译。本项目“jvmjava”是一个开源项目,由Java语言实现,...

    莫枢:JVM是多语言的平台

    Oracle JVM编译器团队工程师莫枢在《JVM——多语言的平台》的演讲中,提到Java最强悍的地方在于编译器非常便于应用。而最初定位在为Java提供丰富的功能平台JVM后期走了更为宽广的路线,支持更多的独立语言。有微博...

    学习笔记——JVM性能调优之 jstat(csdn)————程序.pdf

    JVM性能调优是Java开发中至关重要的一环,它直接影响应用程序的运行效率和稳定性。jstat(JVM Statistics Monitoring Tool)是Oracle JDK提供的一款强大的命令行工具,用于实时监控Java虚拟机的各种运行状态,包括...

    推荐一些JVM原理,JVM调优,JVM内存模型,JAVA并发 电子书1

    标题中提到了JVM原理、JVM调优、JVM内存模型和JAVA并发,这些都是Java虚拟机(JVM)相关的核心概念。JVM是运行Java字节码的虚拟计算机,为Java提供了一个跨平台的环境,确保Java程序可以在不同的操作系统上运行而...

    jvm.rar_jvm

    Java虚拟机(JVM)是Java程序运行的基础,它...通过阅读《JVM详解.pdf》、《java内存模型.pdf》、《慢慢琢磨jvm.pdf》和《JVM调优总结.pdf》,我们可以更全面地掌握JVM的工作原理,提升我们的Java编程和系统调优能力。

    jdk,jvm源码

    Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,为Java应用程序提供了一个跨平台的运行环境。JDK(Java Development Kit)包含了开发和运行Java程序所需的所有工具,包括JVM。当我们谈论"jdk,jvm...

    java jvm-old gc耗时几十s,导致系统告警(csdn)————程序.pdf

    在Java应用程序中,JVM(Java虚拟机)的垃圾收集(Garbage Collection, GC)是自动管理内存的关键机制。当对象不再被引用时,GC负责回收这些无用的对象所占用的内存空间,以避免内存泄漏。然而,如果GC过程耗时过长...

Global site tag (gtag.js) - Google Analytics