参考文章:
- 《Java Se11 虚拟机规范》
- 《深入理解Java虚拟机-JVM高级特性与最佳实践 第3版》- 周志明
本文基于Java Se 11讲解。
根据《Java虚拟机规范》的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域:

对于不同的虚拟机实现,在运行时数据区的实现上并不完全相同。对于常用的HotSpot虚拟机来说,它的运行时数据区如下:

主要区别在于,HotSpot使用了直接使用本地内存(即机器本身内存)的元空间(metaspace)来实现方法区。
下面针对每个具体的数据区域进行详细的介绍。
1. 程序计数器
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。
JVM可以同时支持多个执行线程。每个Java虚拟机线程都有自己的pc(程序计数器)寄存器。在任何时候,每个Java虚拟机线程都在执行单个方法的代码,即该线程的当前方法。如果该方法不是native方法,则pc寄存器包含当前正在执行的Java虚拟机指令的地址。如果线程当前正在执行的方法是native的,则pc寄存器的值为undefined。Java虚拟机的pc寄存器足够宽,可以容纳特定平台上的returnAddress或native指针。
此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
2. Java虚拟机栈
与程序计数器一样,是线程私有的,生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型。
「虚拟机栈」里面的每条数据就是「栈帧」,在 Java 方法执行的时候则创建一个「栈帧」并入栈「虚拟机栈」。调用结束则「栈帧」出栈。
每个栈帧包含四个区域:
每个线程拥有一个「虚拟机栈」,每个「虚拟机栈」拥有多个「栈帧」,而栈帧则对应着一个方法。每个「栈帧」包含局部变量表、操作数栈、动态链接、方法返回地址。方法运行结束则意味着该「栈帧」出栈。
在《Java虚拟机规范》中,对这个内存区域规定了两类异常状况:
StackOverflowError异常;OutOfMemoryError异常。本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
《Java虚拟机规范》对本地方法栈中方法使用的语言、使用方式与数据结构并没有任何强制规定,因此具体的虚拟机可以根据需要自由实现它,甚至有的Java虚拟机(譬如Hot-Spot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常。
所有线程共享,虚拟机启动时创建。唯一的目的是用于存放对象实例和数组,绝大部分对象实例在堆上分配内存。
在 Java 中,数组也是对象。
现代垃圾收集器大部分基于分代收集理论设计。“新生代”、“老年代”这些名词仅仅是一部分GC的设计风格,而不是《Java虚拟机规范》定义的。而从G1收集器出现之后,出现了不采用分代设计的新垃圾收集器。
JDK8之后Class对象、static变量、字符串常量池都放在堆里。
static变量作为类的信息,存储在Class对象里。
Java 的对象可以分为基本数据类型和普通对象。普通对象会在堆上分配。对于基本数据类型,如果是局部变量,则会在栈上分配。其他情况,通常在在堆上分配,逃逸分析的情况下可能会在栈分配。
如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。
字符串常量池是由String类维护的一个字符串池。是一种池化思想的实现,是为了节省重复创建字符串对象的性能开销和内存空间。
每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。
字符串常量池从JDK7开始挪到了堆中。
可以通过调用String.intern()方法把一个字符串对象放到字符串常量池中。如果池中已经存在相等的对象,则会返回已存在对象的引用;否则会把这个字符串对象加入到池中,并返回新加入的字符串对象的引用。
String s = new String("hello")会创建几个对象?
如果字符串常量池中没有"hello",则生成2个,否则只生成一个。
String s = new String("abc"); System.out.println((s.intern() == s));打印结果是什么?
打印结果为false。s指向的是堆中的对象,s.intern()返回的是字符串常量池中的对象的引用。
字面量(literal) :用于表达源码中的一个固定值的符号(notation)。如整数、浮点数及字符串等。如1、0x01是整数字面量,Hello World是字符串字面量。
常量:在java中,final修饰的变量也可以被称为是常量。任何具有不变性的东西都可以称为常量。如String对象是常量。
对象池:是Java语言层面实现的,如Integer.valueOf()(Integer i = 10也会调该方法)会使用IntegerCache的缓存对象。如果使用new Integer(10)则不会使用对象池中的实例。
字符串常量池:类似于对象池,但它是JVM层面的技术。字符串常量池的实现是c++实现的StringTable,实际上是一个固定容量的Hashtable,每一个bucket包含一系列相同hash码的字符串。
用于存储被JVM加载的class的元数据信息,比如类的结构、运行时的常量池、字段、常量、方法数据、方法构造函数以及接口初始化等特殊方法。还有JIT编译器编译后的代码缓存等数据。
JDK8之前,HotSpot采用永久代的概念实现方法区,JDK8开始废弃了永久代的概念,改用在本地内存(Native Memory)中实现的元空间(Meta-space)来代替。
方法区的GC比较少出现,回收目标主要是针对常量池的回收和对类型的卸载。
根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。
一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。
既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'
是否有可能:before_filter:authenticate_user!||:authenticate_admin! 最佳答案 before_filter:do_authenticationdefdo_authenticationauthenticate_user!||authenticate_admin!end 关于ruby-on-rails-before_filter运行多个方法,我们在StackOverflow上找到一个类似的问题: https://