日期:2014-05-20  浏览次数:20904 次

说说虚拟机的相关知识点--适合初学者
[align=center]JVM相关知识
一什么是虚拟机
深入虚拟机解释:
当你说“java虚拟机”时,可能指的是如下三种不同的东西:
·抽象规范
·一个具体的实现
·一个运行中的虚拟机规范
我们口头中讨论的更多的是jdk自己实现的虚拟机,研究的更多的是运行中的虚拟机。
Java虚拟机什么时候出现
当你运行一个java程序的时候就会出现,如果同一台计算机同时运行三个java程序,将得到三个java虚拟机实例。注意是 实例。每一个java程序都运行在于它自己的虚拟机实例中。
我们简单的认为调用 public static void main(String[] args)就启动了一个java程序。过程如下:
当我们使用命令的来执行一个java程序的时候,例如:Test.class java Test
1 java.exe 会帮我们找到JRE,接着找到位于JRE内部的jvm.dll(你可以去找找你的jre\bin\client\jvm.dll)我的是开发机。所以是client。还有server的。
虚拟机激发后,会先做一些初始化的动作,比如说读取系统参数等,一旦初始化动作完成之后,就会开始张载class文件了。

Java Heap分为2个区1.Young2.Old
当然划分更小的有TLA。
JVM的Heap分配可以使用-X参数设定,
-Xms 启动JVM申请的最小Heap内存,默认为物理内存的1/64但小于1GB。-Xmxjava heap为JVM可申请的最大Heap内存,默认为物理内存的1/4但小于1GB。
PS:
默认当空余堆内存少于40%时,JVM会增大Heap到-Xmx指定的大小,这个比例可以通过
参数-XX:MinHeapFreeRation 改变。
当空余内存大于70%时候,JVM会减少到Heap的大小到-Xms指定的大小。
可以通过参数-XX:MaxHeapFreeRation来指定这个比例。
所以对于运行的系统来说,为避免在运行的时候频繁调整Heap,建议将-Xms和
-Xmx配置成一样大小。


一 Yong区


图片来自互联网!
有关这几个区简单的描述一下:
先分为两大区:一为新生代,二为旧设生代
新生代分为几个块:
Eden,form,to 如上图所示。
一般新产生的对象就放在这里,为什么说一般呢?因为有特殊的例子:
第一: 大对象, 这个你的对象大小大于-XX:PreternureSizeThreshold(单位为字节,默认为0,注意此参数在新生代采用Parallel Scavenge GC时无效)。
这个参数,那么大对象直接在养老区分配,不在Eden分配。
注意;以上的情况是以不同的GC来说的。
第二:
另一种为大的数据对象,而且数组中没有引用外部对象。
旧生代就是用于存放新生代经过垃圾收集后的存活对象。

下面详细说说:
一,对于新生代选用串行(Serial GC)的话:
1 如果新产生的对象大于Eden,对象直接在旧生代分配
2如果配置了PreternureSizeThreshold参数,新产生的对象大于这个参数的值,
对象也会在旧生代直接分配。
二,对于新生代选用并行回收GC(Parallel Scavenge)
PS为server模式时默认选择的GC方式,
当TLAB,Eden上分配都失败时,判断需要分配的内存大小是否>=Eden space的一半,
如是就直接在旧生代分配。



三,新生代选用并行GC(ParNew)
其内存分配跟串行GC的一样。
在这里啰嗦的解释一下TLAB这个东西:
TLAB全称叫 Thread Local Allocation Buffer),Sun JDK为了提高对象生成的效率,在Eden哪里为每一个新创建的线程创造一个这么样的空间。TLAB的大小一般都是由JVM 运行计算得出的,但是可以通过-XX:TLABWasteTargetPercent来设置TLAB可占用的Eden Space
的百分比,默认值为1%。
在TLAB分配内存时不需要加锁,因为在堆上分配内存时候要加锁,所以在TLAB分配时效率快很多。当然 这只是适合小对象分配。其分配细节取决于具体的GC。

以上就是Java的内存管理了,当然这是很比较简单的描述的,因为本文的预期读者为刚学java不久的学生,所以就不写那么广泛了(高深的东西我也不懂!哈哈)
还有一个区,不得不说的一个区就是持久区,也就是方法区。
方法区
方法区存放了要加载的重要的类的信息,反射就是靠这个东西的。
里面存了什么东西呢?大概有这些东西:类的静态变量,类中定义为final类型的常量,
类中的Field信息,类中的方法信息。这里怎么比如呢?
假设对象是一辆宝马的话,那么方法区就存放了宝马的各种组件的模具。
你要一辆宝马的话,根据模具生产出来零件拼装就是了。只不过是jvm帮我们拼装而已。
PC寄存器
这个东西每一个线程都会有的,最简单的Hello,World!也会有的,main就是方法就是在主线程里。
为什么要这个东西呢?没有学过汇编的童鞋可能有点晕啊!
现在的计算机都是多任务的,多线程的,或者多进程的。一般来说,每一个线程或者进程都会获得操作系统分给它的一个固定的CPU运行时间片。如果时间片用完了,就算任务没有完成,也得必须交出CPU。
如果一个线程在运行到某个的时候,分给线程的CPU时间片用完了,线程肯定被交出CPU了,然后又去排队等待CPU。那么如果它再次获得CPU的时候,难道又重新去重头运行一篇?NO,这个PC寄存器就发挥作用了,PC寄存器保存着上次被移出CPU时候,运行完的指令的下一条代码。假设运行到A指令,线程就被移出CPU了,那么pc寄存器指向的就是A指令的下一条指令,假设是B,等到重新获取到CPU了,直接就运行指令B了。线程的换入换出是比较复杂的过程。所以这里不做深入的描述了。

本地方法栈
调用本地方法的时候用到的,例如java里面调用C代码的时候。

JAVA栈
保存着调用方法时候的一些信息,
每启动一个线程,JVM都会为它分配一个Java栈,用于存放方法中的局部变量,操作数以及异常数据等。当线程调用某个方法时,JVM会根据方法区中该方法的字节码组建一个栈帧。并将该栈帧压入Java栈中,方法执行完毕时,JVM会弹出该栈帧并释放掉。
注意,Java栈中的数据是线程私有的,一个线程是无法访问另一个线程的Java栈的数据。这也就是为什么多线
程编程时,两个相同线程执行同一方法时,对方法内的局部变量时不需要数据同步的原因。


类是怎么装载入内存的?
准确的说是class文件如何加载进虚拟机的。下面也简单的介绍一下:
类加载器基本概念
顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。
基本上所有的类加载器都是 java.lang.ClassLoader 类的一个实例。
加载器是层次划分的:

该图来自IBM的文章
刚学习的同学知道有这些加载类就可以了,至于加载的顺序和模式,如果大家想知道的话,不妨去je查查资料或者看看深入虚拟机吧。
当我们使用命令来执行某一个Java程序(比如HelloWorld.class)的时候:java HelloWorld
(1) java.exe 会帮助我们找到 JRE ,接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器 , 最后加载动态库,激活 Java 虚拟机器。
(2) 虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。一旦初始化动作完成之后,就会进行类的加载了。
(3)当类加载到内存后,虚拟机开始对其进行检验,准备和解析了。[/align]

------解决方案--------------------
楼主这是在扫盲?