Java虚拟机学习



楼主 collmakkaksm  发布于 2016-04-27 17:51:00 +0800 CST  
想开一个贴子介绍Java虚拟机,不知道有没有人看

楼主 collmakkaksm  发布于 2016-04-27 17:51:00 +0800 CST  


楼主 collmakkaksm  发布于 2016-04-27 17:51:00 +0800 CST  
有没有人想看

楼主 collmakkaksm  发布于 2016-04-27 17:57:00 +0800 CST  


楼主 collmakkaksm  发布于 2016-04-27 17:57:00 +0800 CST  
如2楼体系结构图。
类加载器(ClassLoader)用来加载 class字节码到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源文件在经过 Javac之后就被转换成 Java 字节码文件(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每一个这样的实例用来表示一个 Java 类。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载。





楼主 collmakkaksm  发布于 2016-04-27 22:11:00 +0800 CST  


系统提供的类装载器主要由下面三个:
启动类加载器(bootstrap classloader):它用来加载 Java 的核心库,是用原生代码(本地代码,与平台有关)来实现的,并不继承自java.lang.ClassLoader。这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识加的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
扩展类加载器(extensions classloader):扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量java.ext.dir 指定位置中的类库加载到内存中
应用程序类加载器(application classloader):系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,由于这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序默认的类加载器。
用户自定义的类装载器
用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其它对象一样,用户自定义的类装载器以有Class类的实例都放在内存中的堆区,而装载的类型信息则都放在方法区。


对于初次了解Java虚拟机的同学,只需要大致记住有几类加载器以及他们的作用即可,具体使用的时候可以查阅资料,用的多就会了

楼主 collmakkaksm  发布于 2016-04-27 22:17:00 +0800 CST  
Java运行数据区域也就是Java内存区,常常出现内存异常的地方(OOM等),也是我们最关心的一个地方。
大家说到内存往往会想到“栈”和“堆”,但Java虚拟机内存并不是仅仅只有这两个区域。 下面依次介绍java虚拟机内存的各个区域,介绍这些区域的作用、服务对象以及其中可能产生的问题。

楼主 collmakkaksm  发布于 2016-04-27 22:25:00 +0800 CST  


楼主 collmakkaksm  发布于 2016-04-27 22:26:00 +0800 CST  
程序计数器Program Counter Register(PRC)
如果大家了解过相对底层一点的东西如汇编,嵌入式等相关学习,对程序计数器PRC应该不会陌生,当然在Java里面程序计数器也有它的作用。
一、在Java内存里面程序计数器只占一块很小的区域。
二、它的作用可以看作是当前线程所执行的字节码的行号指示器,通过改变PRC的值进行字节码寻指。分支、循环、跳转、异常处理等都是依赖它完成。
三、每一条线程都拥有一个自己私有的PRC,且每个CRC互不干扰。同时我们可以将CRC称为“线程私有”的内存。
四、如果虚拟机正在执行Java方法,PRC记录的是正在执行的虚拟机字节码指令的地址。如果执行的是native方法,那么PRC为空。
五、PRC是唯一一个在内存区域不会出现OutOfMemoryError的地方

楼主 collmakkaksm  发布于 2016-04-27 22:39:00 +0800 CST  
如果有看,觉得有用,希望回个帖,有问题也希望回个贴。不然都不知道有没有人看。

楼主 collmakkaksm  发布于 2016-04-27 22:41:00 +0800 CST  
Java虚拟机栈(Java Virtual Machine Stacks )
虚拟栈主要描述的是Java方法执行的内存模型(你需要明白:每一个Java线程会有(私有)一个相对应的虚拟机栈区,栈的一段区域叫做栈帧):每个方法被执行一次,都会在栈区内创建一个栈帧(Stack Frame),用于存储局部变量表,操作栈、动态链接、方法出口等信息。每一个方法的执行到完成过程代表着一个栈帧的入栈和出栈过程。
局部变量表存放着编译期可知的基本数据类型(boolean,byte、char、short、int、float、long、double)、对象引用和returnAddress(指向一条字节码指令地址)。其中long和double占两个局部变量空间(Slot),其余数据类型只占一个。局部变量表的内存空间大小在编译期间就已经分配,在方法执行的过程中不会发生变化。
在Java虚拟机规范中,在这个区域规定了两种内存异常:
StackOverflowError:栈的请求深度大于虚拟机的允许最大深度。
OutOfMemoryError:(大部分Java虚拟机都允许在内存不够的情况下,动态扩展内存,同时也允许固定内存长度的虚拟机栈),当扩展时无法申请到足够的内存就会抛出此异常。

楼主 collmakkaksm  发布于 2016-04-27 23:11:00 +0800 CST  
明日再来哈

楼主 collmakkaksm  发布于 2016-04-27 23:14:00 +0800 CST  
本地方法栈(Native Method Stacks)
跟虚拟机栈相似,区别在于Java虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机使用到的Native方法服务。虚拟机规范里对本地方法栈,没有具体的语言,使用方式等进行限制,因此虚拟机可以自由地实现它。
抛出异常:
StackOverflowError:栈的请求深度大于虚拟机的允许最大深度。
OutOfMemoryError:(大部分Java虚拟机都允许在内存不够的情况下,动态扩展内存,同时也允许固定内存长度的虚拟机栈),当扩展时无法申请到足够的内存就会抛出此异常。


楼主 collmakkaksm  发布于 2016-04-28 11:00:00 +0800 CST  
Java堆(heap)
Java堆是Java虚拟机所管理内存的最大一块区域,在虚拟机启动时创建。唯一目的是存放对象实例,几乎所有的对象实例都在这里分配内存。
Java堆是垃圾收集器管理的主要区域,因此很多时候被称为GC堆(Garbage Collected heap,“垃圾堆”),他是一个物理上不连续的内存空间只是逻辑上连续。
如果堆内存无法扩展将会抛出OOM异常。(关于具体的回收机制等后面会慢慢讲述)

楼主 collmakkaksm  发布于 2016-04-28 11:21:00 +0800 CST  
方法区(Method Area)
方法区和Java堆一样,是各个线程共享的一个内存区域。主要用来存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范中把方法区描述为堆的逻辑部分,但是它还有一个别名Non-Heap(非堆),目的就是为了将它与堆区分。
垃圾收集行为在方法区是比较少见的,在这个区域里面内存回收的目标主要是针对常量池和类型(自定义的类加载器加载的类的类可以被卸载的)卸载的。
运行时常量池(Runtime Constant Pool)是方法区的一部分,用于存放编译期产生生成的各种字面量和符号引用(加载后放入)。
抛出OOM异常

楼主 collmakkaksm  发布于 2016-04-28 21:17:00 +0800 CST  
直接内存(Direct Memory)
不属于运行时是数据区的一部分,也不是虚拟机规范的内存区域,但这一部分也常常被使用,而且抛出OOM。
在JDK1.4中新加入了NIO(new input/output),引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用本地方法库直接分配内存,然后通过一个存储在java heap 里的DirectByteBuffer对象作为这快内存的引用进行操作。

楼主 collmakkaksm  发布于 2016-04-28 21:25:00 +0800 CST  
上面已经将Java虚拟机运行时数据区的各区域的作用,存储内容,以及抛出的异常等做出了介绍(更具体的介绍,大家可以自己搜索下)。其中程序计数器,虚拟机栈,本地方法栈随线程生,随线程亡;栈中的栈帧随着方法的执行和退出而有条不絮的进行着出栈和进栈。每一个栈帧中分配多少内存,基本上在类的结构确定下来就已知了,因此这几个区域内存的分配和回收都具有确定性,因为在方法结束或者线程结束,内存自然而然的跟着回收了。
Java堆和方法区则不一样,一个接口中的多个类的实现,一个方法多个分支,多可能造成内存不一样,不确定性,只有当程序处于运行时我们才知道,有多少个对象被创建,这部分的内存分配和回收都是动态的。因此接下来主要介绍这一部分的垃圾回收与内存分配

楼主 collmakkaksm  发布于 2016-04-28 21:56:00 +0800 CST  
人工自顶

楼主 collmakkaksm  发布于 2016-04-28 21:57:00 +0800 CST  
对象是否死亡?
堆中几乎存放这Java世界里的所有实例对象,垃圾收集器在对堆进行回收前,第一件事就是确定那些对象还“存活”着。
下面主要介绍:
引用计数算法
根搜索算法


楼主 collmakkaksm  发布于 2016-04-28 22:03:00 +0800 CST  

楼主:collmakkaksm

字数:12910

发表时间:2016-04-28 01:51:00 +0800 CST

更新时间:2021-02-23 17:47:55 +0800 CST

评论数:232条评论

帖子来源:百度贴吧  访问原帖

 

热门帖子

随机列表

大家在看