JVM一点一滴

参考博文:http://www.cnblogs.com/sunada2005/p/3577799.html

    http://www.importnew.com/17770.html

线程一些概念

在应用程序运行过程中,jvm允许一个应用并发执行多个线程,其实jvm中的java线程跟远程操作系统的线程有着直接的映射关系。当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。run() 返回时,被处理未捕获异常,原生线程将确认由于它的结束是否要终止 JVM 进程(比如这个线程是最后一个非守护线程)。当线程结束时,会释放原生线程和 Java 线程的所有资源。

线程的相关组件

程序计数器(PC)

PC 指当前指令(或操作码)的地址,本地指令除外。如果当前方法是 native 方法,那么PC 的值为 undefined。所有的 CPU 都有一个 PC,典型状态下,每执行一条指令 PC 都会自增,因此 PC 存储了指向下一条要被执行的指令地址。JVM 用 PC 来跟踪指令执行的位置,PC 将实际上是指向方法区(Method Area)的一个内存地址。

栈(Stack)

每个线程拥有自己的栈,栈包含每个方法执行的栈帧。栈是一个后进先出(LIFO)的数据结构,因此当前执行的方法在栈的顶部。每次方法调用时,一个新的栈帧创建并压栈到栈顶。当方法正常返回或抛出未捕获的异常时,栈帧就会出栈。除了栈帧的压栈和出栈,栈不能被直接操作。所以可以在堆上分配栈帧,并且不需要连续内存。

Native栈

并非所有的 JVM 实现都支持本地(native)方法,那些提供支持的 JVM 一般都会为每个线程创建本地方法栈。如果 JVM 用 C-linkage 模型实现 JNI(Java Native Invocation),那么本地栈就是一个 C 的栈。在这种情况下,本地方法栈的参数顺序、返回值和典型的 C 程序相同。本地方法一般来说可以(依赖 JVM 的实现)反过来调用 JVM 中的 Java 方法。这种 native 方法调用 Java 会发生在栈(一般是 Java 栈)上;线程将离开本地方法栈,并在 Java 栈上开辟一个新的栈帧。

堆内存

1.什么是堆内存?

堆内存是是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。

2.堆内存的特点是什么?

第一点:堆其实可以类似的看做是管道,或者说是平时去排队买票的的情况差不多,所以堆内存的特点就是:先进先出,后进后出,也就是你先排队,好,你先买票。

第二点:堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

3.new对象在堆中如何分配?

由Java虚拟机的自动垃圾回收器来管理

栈内存

1.什么是栈内存

栈内存是Java的另一种内存,主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量

2.栈内存的特点

第一点:栈内存就好像一个矿泉水瓶,像里面放入东西,那么先放入的沉入底部,所以它的特点是:先进后出,后进先出

第二点:存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性

3.栈内存分配机制

栈内存可以称为一级缓存,由垃圾回收器自动回收

栈和堆的区别

JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

差异

1.堆内存用来存放由new创建的对象和数组。 
2.栈内存用来存放方法或者局部变量等 
3.堆是先进先出,后进后出 
4.栈是后进先出,先进后出

相同

1.都是属于Java内存的一种 
2.系统都会自动去回收它,但是对于堆内存一般开发人员会自动回收它

 方法区(Method Area)

Object Class Data(加载类的类定义数据) 是存储在方法区的。除此之外,常量静态变量、JIT(即时编译器)编译后的代码也都在方法区。正因为方法区所存储的数据与堆有一种类比关系,所以它还被称为 Non-Heap。方法区也可以是内存不连续的区域组成的,并且可设置为固定大小,也可以设置为可扩展的,这点与堆一样。
垃圾回收在这个区域会比较少出现,这个区域内存回收的目的主要针对常量池的回收和类的卸载。
 
运行时常量池(Runtime Constant Pool)
 
方法区内部有一个非常重要的区域,叫做运行时常量池(Runtime Constant Pool,简称 RCP)。在字节码文件(Class文件)中,除了有类的版本、字段、方法、接口等先关信息描述外,还有常量池(Constant Pool Table)信息,用于存储编译器产生的字面量和符号引用。这部分内容在类被加载后,都会存储到方法区中的RCP。值得注意的是,运行时产生的新常量也可以被放入常量池中,比如 String 类中的 intern() 方法产生的常量。
  常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)对其他类型、方法、字段的符号引用.例如:
◆类和接口的全限定名;
◆字段的名称和描述符;
◆方法和名称和描述符。
  池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用.
  很有用且重要关于常量池的扩展:Java常量池详解 http://www.cnblogs.com/DreamSea/archive/2011/11/20/2256396.html

 jvm运行过程

 

看 JVM 是如何运行的,也就是输入 java JVMShow 后,我们来看 JVM 是如何处理的:
     第 1 步 、向操作系统申请空闲内存。JVM 对操作系统说“给我 64M(随便模拟数据,并不是真实数据) 空闲内存”,于是,JVM 向操作系统申请空闲内存作系统就查找自己的内存分配表,找了段 64M 的内存写上“Java 占用”标签,然后把内存段的起始地址和终止地址给 JVM,JVM 准备加载类文件。
     第 2 步,分配内存内存。JVM 分配内存。JVM 获得到 64M 内存,就开始得瑟了,首先给 heap 分个内存,然后给栈内存也分配好。
     第 3 步,文件检查和分析class 文件。若发现有错误即返回错误。
     第 4 步,加载类。加载类。由于没有指定加载器,JVM 默认使用 bootstrap 加载器,就把 rt.jar 下的所有类都加载到了堆类存的Method Area,JVMShow 也被加载到内存中。我们来看看Method Area区域,如下图:(这时候包含了 main 方法和 runStaticMethod方法的符号引用,因为它们都是静态方法,在类加载的时候就会加载
Heap 是空,Stack 是空,因为还没有对象的新建和线程被执行。
      第 5 步、执行方法。执行 main 方法。执行启动一个线程,开始执行 main 方法,在 main 执行完毕前,方法区如下图所示:
public final static String ClASS_CONST = "I'm a Const";  
     在 Method Area 加入了 CLASS_CONST 常量,它是在第一次被访问时产生的(runStaticMethod方法内部)。
 
     堆内存中有两个对象 object 和 showcase 对象,如下图所示:(执行了JVMShowcase showcase=new JVMShowcase();  )
为什么会有 Object 对象呢?是因为它是 JVMShowcase 的父类,JVM 是先初始化父类,然后再初始化子类,甭管有多少个父类都初始化。
 
在栈内存中有三个栈帧,如下图所示:
于此同时,还创建了一个程序计数器指向下一条要执行的语句。
 
第 6 步,释放内存。释放内存。运行结束,JVM 向操作系统发送消息,说“内存用完了,我还给你”,运行结束。
--------------------------------------------------------------------------------------------
现在来看JVM内存是如何分配的,该部分转载来自 http://blog.csdn.net/shimiso/article/details/8595564
posted @ 2017-07-26 15:09  青春不打烊  阅读(168)  评论(0编辑  收藏  举报