java虚拟机的原理

所谓虚拟机,就是一台虚拟的机器,它是一款软件,用来执行一系列虚拟计算机指令,是程序与底层操作系统和硬件无关的关键所在。

java虚拟机的体系结构

JVM

基本概念说明

1、类加载子系统:负责从文件或网络中加载class信息,JVM的类加载是通过ClassLoader及其子类来完成的,加载的信息放在一块称之为方法区的内存空间。

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

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

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

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

6、本地方法栈:和java栈非常类似,最大的不同为本地方法栈用于本地方法调用。java虚拟机允许java直接调用本地方法。

7、垃圾收集系统:是java的核心,也是必不可少的,java有一套自己进行垃圾清理的机制,开发人员无需手工清理。

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

9、执行引擎:虚拟机最核心的组件就是执行引擎了,它负责执行虚拟机的字节码,一般会先进行编译成机器码后执行。

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

堆解决的是数据存储的问题,即数据怎么放,放在哪儿。 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。 方法区则是辅助堆栈的永久区(Perm),解决堆栈信息的产生,是先决条件。

我们创建一个新的对象,Person 那么Person类的一些信息(类信息,静态信息都存在方法区中) 而Person类被实例化出来后,则存储到java堆中的一块内存空间; 当我们去使用的时候,都是使用Person对象的引用,eg:Person psn= new Person(); 这里psn就是存放在java栈中的,即Person真实对象的一个引用。

java堆

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

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

新生代分为eden区,S0区,S1区,S0和S1也被村委from区和to区,他们是两块大小相等并且可以互换角色的空间。

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

java栈

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

  • 局部变量表:用于报错函数的参数及局部变量。
  • 操作数栈:主要保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
  • 帧数据区:除了局部变量表和操作数栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据区保存着访问常量池的指针,方便程序访问常量池,另外,当函数返回或出现异常时,虚拟机必须有一个异常处理表,方便发送异常的时候找到异常代码,因此异常处理表也是帧数据区的一部分。

java方法区

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

虚拟机参数

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

堆分配参数(一)

 -XX:+PrintGC 使用这个参数,虚拟机启动后,只要遇到GC就会打印日志。
 
 -XX:+UseSerialGC 配置串行回收器。

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

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

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

 -Xmn 设置新生代大小

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

  -XX:NewSize  初始时新生代大小.通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%

 -XX:MaxPermSize:最大方法区大小

 -XX:PermSize:初始时方法区大小

  -XX:NewRatio 新生代与老年代的比例,如 –XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3

  -XX:SurvivorRatio 新生代中 Eden 与 Survivor 的比值。默认值为 8。即 Eden 占新生代空间的 8/10,另外两个 Survivor 各占 1/10

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

堆分配参数(二)

新生代的配置

-Xmn:可以设置新生代的大小,设置一个比较大的新生代会减少老年代的大小,这个参数对系统性能以及GC行为有很大的影响,新生代大小一般会设置整个堆空间的1/3~1/4左右。 -XX:SurvivorRatio:用来设置新生代eden空间和from/to空间的比例。含义:-XX:SurvivorRatio=eden/from=eden/to

** 总结**:不同的堆分布情况,堆系统执行会产生一定的影响,在实际工作中,应该根据系统的特点做出合理的配置,基本策略:尽可能将对象预留在新生代,减少老年代的GC次数。 除了可以设置新生代的绝对大小(-Xmn),还可以使用(-XX:NewRatio)设置新生代和老年代的比例:-XX:NewRratio=老年代/新生代

堆溢出处理

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

dump文件可以使用内存分析工具:Memory Analyzer 1.5.0

栈配置

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

方法区

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

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

转载于:https://my.oschina.net/u/2283517/blog/1575752

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值