所谓虚拟机,就是一台虚拟的机器。它是一款软件,用来执行一系列虚拟计算机指令,大体上虚拟机可以分为系统虚拟机和程序虚拟机,Visual Box 、Vmare就属于系统虚拟机。他们完全是对物理计算机的仿真,提供了一个可运行完成操作系统的软件平台。程序虚拟机典型代表就是java虚拟机,它专门为执行单个计算机程序而设计,在java 虚拟机中执行的指令,我们称为java字节码指令。无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。java发展至今,出现过很多虚拟机,最初Sun使用的一款叫Classic的Java虚拟机,到现在引用最广泛的是HotSpot虚拟机,除了Sun以外,还有BEA的JRockit,目前JRockit和HotSpot都被Oracle收入旗下,有整合的趋势。

Java虚拟机的基本结构

1 类加载子系统 : 负责从文件系统或者网络中加载Clsaa信息,加载的信息存放在一块称之为方法区的内存空间。

2方法区:就是存放类信息、常量信息、常量池信息、包括字符串字面量和数字常量等。

3java堆:在java虚拟机启动的时候建立java堆,他是java程序最主要的内存工作区域,几乎所有的对象实例都存放到java堆中,堆空间是所有线程共享的。

4直接内存:Java的NIO库允许java程序使用直接内存,从而提高性能,通常直接内存速度会优于java堆。读写频繁的场合可能会考虑使用。

5java栈:每个虚拟机线程都有一个私有的栈,一个线程的java栈在线程创建的时候被创建,java栈中保存着局部变量,方法参数、同时java的方法调用、返回值等。

6本地方法栈:本地方法栈和java栈非常类似,最大不同为本地方法栈用于本地方法调用。java虚拟机允许java直接调用本地方法(通常使用C编写)。

7垃圾收集系统是java的核心,也是必不可少的,java有一套自己进行垃圾清理的机制,具体的待会说明。

8PC 寄存器也是每个线程私有的空间,java虚拟机会为每个线程创建PC寄存器,在任意时刻,一个java线程总是在执行一个方法,这个方法被称为当前方法,如果当前方法不是本地方法,PC寄存器就会执行当前正在被执行的命令,如果是本地方法,则PC寄存器值为undefined,寄存器存放如当前执行环境指针,程序计数器,操作栈指针,计算的变量指针等信息。

9虚拟机最核心的组建就是执行引擎了,它负责执行虚拟机的字节码。一般用户先进行编译称机器码后执行。

堆、栈、方法区概念和联系

堆解决的是数据存储的问题,即数据怎么放,放在哪儿。

栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。

方法区则是辅助堆栈的快永久区(Perm),解决堆栈细心的产生,是先决条件。

我们创建一个新的对象,User:那么User类的一些信息(类信息,静态信息都存在于方法区中)

而User类被实例化出来之后,被存储到java堆中,一块内存空间

当我们去使用的时候,都是使用User对象的引用,形如User user = new User();

辨清java堆

java堆是和java应用程序关系最密切的内存空间,几乎所有的对象都存放其中,并且java堆完全是自动化管理的,通过垃圾回收机制,垃圾对象会自动清理,不需要显示地释放。

根据垃圾回收机制不同,Java堆有可能有不同的结构。最常见的就是将整个java堆分为新生代和老年代。其中新生代存放新生的对象或者年龄不大的对象,老年代则存放老年对象。

新生代分为eden区、s0区、s1区,s0和s1也被称为from和to区域,他们是两块大小相等并且可以互换角色的空间。

绝大多数情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,则会进入s0和s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1,当对象达到一定年龄后,则进入老年代。

垃圾收集算法

引用计数法:这是个比较古老而经典的垃圾收集算法,其核心就是在对象被其他所引用时计数器加1,而当引用失效时则减1,但是这种方式有非常严重的问题:无法处理循环引用的情况、还有就是每次进行加减操作比较浪费系统性能。

标记清除法;就是分为标记和清楚两个阶段进行处理内存中的对象,当然这种方式也有非常大的弊端,就是空间碎片问题,垃圾回收后的空间不是连续的,不连续的内存空间的工作效率要低于连续的内存空间。

复制算法:其核心思想就是将内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存留对象复制到未被使用的内存块中去,之后去清楚之前正在使用的内存块中所有的对象,反复去交换两个内存的角色,完成垃圾收集。(java中新生代的from和to空间就是使用这个算法)

标记压缩法:标记压缩法在标记清除法基础之上做了优化,把存活的对象压缩到内存一端,而后进行垃圾清理。(java中老年代使用的就是标记压缩法)/

java栈

java栈是一块线程私有的内存空间,一个栈,一般由三部分组成:局部变量表,操作数栈和帧数据区。

局部变量表:用于报错函数的参数及局部变量:

操作数栈:主要保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间;

帧数据区:除了局部变量表和操作数栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据保存着访问常量池的指针,方便程序访问常量池,另外,当函数返回或者出现异常时,虚拟机必须由一个异常处理表,方便发送异常的时候找到异常的代码,因此异常处理表也是帧数据区的一部分。

java方法区

java方法区和堆一样,方法区是一块所有线程共享的内存区域,它保存系统的类信息,比如类的字段,方法,常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存移除错误。方法区可以理解为永久区(Perm)。

虚拟机参数

在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障排查会有一定的帮助,为此,虚拟机提供了一些跟踪系统状态的参数,使用给定的参数执行java虚拟机,就可以在系统运行时打印相关日志,用于分析实际问题。我们进行虚拟机参数配置,其实主要就是围绕着堆,栈,方法区进行配置。

堆分配参数

-XX:+PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志。

-XX:+UseSerialGC 配置串行回收器

-XX:+PrintGCDetails 可以查看详细信息,包括各个区的情况。

-Xms: 设置java程序启动时初始堆大小

-Xmx: 设置java程序能获得的最大堆大小

-Xmx20m -Xms5m -XX:+PrintCommandLineFlags: 可以将隐式或者显示传给虚拟机的参数输出

在实际工作中,可以直接将初始的堆大小和最大堆大小设置相等,这样的好处是可以减少程序运行时的垃圾回收次数,从而提高性能。

下面看一个demo:

使用jdk自带的工具类,打印运行时的内存相关的内容,

先看一下没有配置jvm的打印结果:

内存什么的都很大,看不出具体的内存使用情况,下面在看一下使用jvm配置参数后的打印结果,

先看一下怎么设置jvm参数,

设置玩运行时的JVM,在来看一下打印的结果:

可以看到java堆的初始内存时5M,最大内存时20M,第一次分配1M时,使用一开始的初始内存,后来,需要再次分配4M的时候,java虚拟器又重新申请了内存。可以从total_memory看出来。

Heap里面可以看的到新生代,老年代,永久区的空间大小和临界值。

实际项目中,会把jvm的运行环境放在项目运行的容器中。

新生代的堆参数配置

-Xmn:可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为由很大的影响,新生代大小一般会设置整个堆空间的1/3到1/4左右。

-XX:SurvivorRatio:用来设置新生代中eden空间和from/to空间的比例。含义:-XX:SurvivorRatio=eden/from=eden/to

不同的堆分布情况,对系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。

除了可以设置新生代的绝对大小(-Xmn),还可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

这个同样看一个demo:

将3次配置,分别进行jvm配置,然后看一下控制台的信息,能看到前两次eden 和 s0或s1都是2:1的内存空间,

第三次,老年代:新生代也是2:1的内存空间。通过这个配置,可以将JVM的的配置进行细化。

堆溢出处理

在java程序的运行过程中,如果堆空间不足,则会抛出内存溢出的错误(Out Of Menory) OOM,一旦这类问题发生在生产环境,可能引起严重的业务中断,java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError,使用该参数可以在内存溢出时导出整个堆消息,与之配合使用的还有参数,-XX:HeapDumpPath,可以设置导出堆的存放路径。

内存分析工具:Memory Analyzer 1.5.0

栈配置

Java虚拟机提供了参数-Xss来指定线程的最大栈空间,整个参数也直接决定了函数可调用的最大深度。

方法区

和Java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,方案区(永久区)可以保存多少信息可以对其进行配置,在默认情况下,-XX:MaxPermSize为64MB,如果系统运行时生产大量的类,就需要设置一个相对合适的方法区,以免出现永久区内存溢出的问题。

-XX:PermSize=64M -XX:MaxPermSize=64M

直接内存配置

直接内存也是java程序中非常重要的组成部分,特别是广泛用在NIO中,直接内存跳过了java堆,是java程序可以直接访问原生堆空间,因此在一定程度上加快了内存空间的访问速度。但是说直接内存一定就可以提高内存访问速度也不见得,具体情况具体分析。

相关配置参数:-XX:MaxDirectMemorySize,如果不设置默认值为最大堆空间,即-Xmx。直接内存使用达到上限时,就会触发垃圾回收,如果不能有效的释放空间,也会引起系统的OOM;

在JKD1.7之后,不用考虑这个。

更多详情可以参照博客

http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html

java虚拟机的原理的更多相关文章

  1. Java虚拟机工作原理

    Java虚拟机工作原理 首先我想从宏观上介绍一下Java虚拟机的工作原理.从最初的我们编写的Java源文件(.java文件)是如何一步步执行的,如下图所示,首先Java源文件经过前端编译器(javac ...

  2. Java虚拟机工作原理详解 (一)

    一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...

  3. Java虚拟机工作原理详解

    原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了 ...

  4. Java虚拟机工作原理具体解释

    一.类载入器 首先来看一下java程序的运行过程. 从这个框图非常easy大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘其中.然后你在命令行中输入 javac YourClass ...

  5. java虚拟机构造原理

    Java虚拟机的生命周期 一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序.程序开始执行时他才运行,程序结束时他就停止.你在同一台机器上运行三个程序,就会有三个运行中的Java虚拟机. ...

  6. [转]java虚拟机工作原理详解

    一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...

  7. Java虚拟机 - 结构原理与运行时数据区域

    http://liuwangshu.cn/java/jvm/1-runtime-data-area.html 前言 本来计划要写Android内存优化的,觉得有必要在此之前介绍一下Java虚拟机的相关 ...

  8. Java虚拟机工作原理详解 ( 二 )

    首先这里澄清两个概念:JVM实例和JVM执行引擎实例,JVM实例对应了一个独立运行的Java程序,而JVM执行引擎实例则对应了属于用户运行程序的线程:也就是JVM实例是进程级别,而执行引擎是线程级别的 ...

  9. 图灵学院笔记-java虚拟机底层原理

    Table of Contents generated with DocToc 一.java虚拟机概述 二.栈内存解析 2.1 概述 2.2 栈帧内部结构 2.2.1 我们来解析一下compute() ...

随机推荐

  1. WebService与RESTful WebService

    Manual Instruction Document Web Service JAX-WS & JAX-RS Author: Liu Xiang Date: 2018/01/12 1. Su ...

  2. svn分支

    在svn上我们除过一般的保存文档外,对于开发source,可以使用 trunk(主线),branch(分线), tag(上线或测试用) 做分支应用开发. trunk上建立代码位置,存放代码. 点击Te ...

  3. 1037B--Reach Median(中位数)

    median 中位数 odd 奇数 even 奇数 You are given an array aa of nn integers and an integer ss. It is guarante ...

  4. SecureCRT使用本地公钥 SSH 免密码登录Linux

    其原理与Linux系统之间的SSH通道原理是一样的 下文中如果创建公钥的格式是:标准公钥和VanDyke私钥格式,需要用ssh-keygen -i -f 转换.如果是OpenSSH密钥格式可直接修改文 ...

  5. matt cutts : try something new for 30 days

    30 天尝试新事物matt cutts : try something new for 30 days[小计划帮你实现大目标] 是否有些事情, 你一直想去做, 但就是没有实现?马特 ?卡茨建议: 尝试 ...

  6. python函数基础:调用内置函数&定义函数

    调用内置函数 有很多内置函数,在使用中需要积累.这里只举两个例子: 分别调用abs和数据类型转换,注意当入参类型错误时候会报错 ''' print('abs(-100)') abs(-100) pri ...

  7. 17.嵌入ace插件

    我们想 在problem-detail上具体显示代码 建一个component 叫 editor 将ace集成上去,算是他的画布吧. 支持各种语言 可以reset  提交写好的代码到server端编译 ...

  8. svg-edit和svg中的自定义属性

    用svg的码农们肯定知道,在path.rect等元数据中会加入一些自定义属性,保存于数据库,但是用svg-edit编辑器时, 读取的时候,无法读取些这些自定义属性.解决办法:找sanitize.js文 ...

  9. Swift字符串常用方法

    1.0 比较两个字符串是否相等 判断字符串相等的方法是: ==. var str1 = "Hello world" var str2 = "Hello world&quo ...

  10. 用工厂模式和策略模式优化过多的if-else

    多个if-else代码: @RunWith(SpringRunner.class) @SpringBootTest public class EducationalBackgroundTest { p ...