为什么80%的码农都做不了架构师?>>>   

简介

Java虚拟机(JVM)是Java应用的运行环境,从一般意义上来讲,JVM是通过规范来定义的一个虚拟的计算机,被设计用来解释执行从Java源码编译而来的字节码。

体系结构

JVM主要有子系统和内存区:

  • Class Loader 类加载器。 用于读入Java源代码并将类加载到数据区。

  • Execution Engine 执行引擎。 执行来自数据区的指令。

  • ‍运行时数据区‍使用的是底层操作系统分配给JVM的内存。(JVM的内存模型)

类加载器(Class Loader)

JVM在下面几种不同的层面使用不同的类加载器:

  • bootstrap class loader(引导类加载器):是其他类加载器的父类,它用于加载Java核心库,并且是唯一一个用本地代码编写的类加载器。

  • extension class loader(扩展类加载器):是bootstrap class loader加载器的子类,用于加载扩展库。

  • system class loader(系统类加载器):是extension class loader加载器的子类,用于加载在classpath中的应用程序的类文件。

  • user-defined class loader(用户定义的类加载器):是系统类加载器或其他用户定义的类加载器的子类。

当一个类加载器收到一个加载类的请求,首先它会检查缓存,确认该类是否已经被加载,然后把请求代理给它的父类。如果父类没能成功的加载类,那么子类就会自己去尝试加载该类。子类可检查父类加载器的缓存,但父类不能看到子类所加载的类。之所类加载体系会这样设计,是认为一个子类不应该重复加载已经被父类加载过的类。

执行引擎(Execution Engine)

执行引擎一个接一个地执行被加载到数据区的字节码。为了保证字节码指令对于机器来说是可读的,执行引擎使用下面两个方法:

  • 解释执行:执行引擎把它遇到的每一条指令解释为机器语言。

  • 即时编译:如果一条指令经常被使用,执行引擎会把它编译为本地代码并存储在缓存中。这样,所有和这个方法相关的代码都会直接执行,从而避免重复解释。

尽管即时编译比解释执行要占用更多的时间,但是对于需要使用成千上万次的方法,只需要处理一次。相比每次都解释执行,以本地代码的方式运行会节约很多执行时间。

JVM规范中并不规定一定要使用即时编译。即时编译也不是用于提高JVM性能的唯一的手段。规范仅仅规定了每条字节码对应的本地代码,至于执行引擎如何实现这一对应过程的,完全由JVM的具体实现来决定。

内存模型(Memory Model)

对象访问(详细分解见《深入理解JVM》)

OutOfMemoryError异常详解

Java内存模型建立在自动内存管理的概念之上。当一个对象不再被一个应用所引用,垃圾回收器就会回收它,从而释放相应的内存。这一点和其他很多需要自行释放内存的语言有很大不同。

JVM从底层操作系统中分配内存,并将它们分为以下几个区域:

  • 堆空间(Heap Space):这是共享的内存区域,用于存储可以被垃圾回收器回收的对象。

  • 方法区(Method Area):这块区域以前被称作“永生代”(permanent generation),用于存储被加载的类。这块区域最近被JVM取消了。现在,被加载的类作为元数据加载到底层操作系统的本地内存区。

  • 本地区(Native Area):这个区域用于存储基本类型的引用和变量。

一个有效的管理内存方法是把对空间划分为不同代,这样垃圾回收器就不用扫描整个堆区。大多数的对象的生命周期都很段短暂,那些生命周期较长的对象往往直到应用退出才需要被清除。

当一个Java应用创建了一个对象,这个对象是被存储到“初生池”(eden pool)。一旦初生池存储满了,就会在新生代触发一次minor gc(小范围的垃圾回收)。首先,垃圾回收器会标记出那些“死对象”(不再被应用所引用的对象),同时延长所有保留对象的生命周期(这个生命周期长度是用数字来描述,代表了期所经历过的垃圾回收的次数)。然后,垃圾回收器会回收这些死对象,并把剩余的活着的对象移动到“幸存池”(survivor pool),从而清空初生池。

当一个对象存活达到一定的周期后,它就会被移动到堆中的老生代:“终身代”(tenured pool)。最后,当终身代被填满时,就会触发一次full gc或major gc(完全的垃圾回收),以清理终身代。

(译者注:一般我们把初生池和幸存池所在的区域合并成为新生代,把终身代所在的区域成为老生代。对应的,在新生代上产生的gc称为minor gc,在老生代上产生的gc称为full gc。希望这样大家在其他地方看到对应的术语时能更好理解)

当垃圾回收(gc)执行的时候,所有应用线程都要被停止,系统产生一次暂停。minor gc非常频繁,所以被优化的能够快速的回收死对象,是新生代的内存的主要的回收方式。major gc运行起来就相对慢得多,因为要扫描非常多的活着的对象。垃圾回收器本身也有多种实现,有些垃圾回收器在一定情况下能更快的执行major gc。

堆的大小是动态的,只有堆需要扩张的时候才会从内存中分配。当堆被填满时,JVM会重新给堆分配更多的内存,直到达到堆大小的上限,这种重新分配同样会导致应用的短暂停止。

线程

JVM是运行在一个独立的进程中的,但它可以并发执行多个线程,每个线程都运行自己的方法,这是Java必备的一个部分。以即时消息客户端这样一个应用为例,它至少运行两个线程。一个线程用于等待用户输入,另一个检查服务端是否有新的消息传输。再以服务端应用为例,有时一个请求可能要涉及多个线程并发执行,所以需要多线程来处理请求。

在JVM的进程中,所有的线程共享内存和其他可用的资源。每一个JVM进程在进入点(main方法)处都要启动一个主线程,其他线程都从主线程启动,成为执行过程中的一个独立部分。线程可以再不同的处理器上并行执行,同样也可以共享一个处理器,线程调度器负责处理多个线程共享一个处理器的情况。

很多应用(特别是服务端应用)会处理很多任务,需要并行运行。这些任务中有些是非常重要的,需要实时执行的。而另外一些是后台任务,可以在CPU空闲时执行。任务是在不同的线程中运行的。举例子来说,服务端可能有一些低优先级的线程,它们会根据一些数据来计算统计信息。同时也会启动一些高优先级的进程用于处理传入的数据,响应对这些统计信息的请求。这里可能有很多的源数据,很多来自客户端的数据请求,每个请求都会使服务端短暂的停止后台计算的线程以响应这个请求。所以,你必须监控在运行的线程数目并且保证有足够的CPU时间来执行必要的计算。

(译者注:这一段在原文中是在性能优化的章节,译者认为这可能是作者的不小心,似乎放在线程的章节更合适。)

JVM性能优化

堆的优化、栈的优化和垃圾回收器的选择。

JVM的性能取决于其配置是否与应用的功能相匹配。尽管垃圾回收器和内存回收进程是自动管理内存的,但是你必须掌管它们的频率。通常来说,你的应用可使用的内存越多,那么这些会导致应用暂停的内存管理进程需要起作用的就越少。

(1)如果垃圾回收发生的频率比你想的要多很多,那么可以在启动JVM的时候为其配置更大的最大堆大小值。堆被填满的时间越久,就越能降低垃圾回收发生的频率。最大堆大小值可以在启动JVM的时候,用-Xmx参数来设定。默认的最大堆大小是被设置为可用的操作系统内存的四分之一,或者最小1GB。

(2)如果问题出在经常重新分配内存,那么你可以把初始化堆大小设置为和最大堆大小一样。这就意味着JVM永远不需要为堆重新分配内存。但这样做就会失去动态堆大小适配的优化,堆的大小从一开始就被固定下来。配置初始化对大小是在启动JVM,用-Xms来设定。默认初始化堆大小会被设定为操作系统可用的物理内存的六十四分之一,或者设置一个最小值。这个值是根据不同的平台来确定的。

(3)如果你清楚是哪种垃圾回收(minor gc或major gc)导致了性能问题,可以在不改变整个堆大小的情况下设定新生代和老生代的大小比例。对于需要产生大量临时对象的应用,需要增大新生代的比例(当然,后果是减小了老生代的大小)。对于长生命周期对象较多的应用,则需增大老生代的比例(自然需要减少新生代的大小)。以下几种方法可以用来设定新生代和老生代的大小:

  • 在启动JVM时,使用-XX:NewRatio参数来具体指定新生代和老生代的大小比例。比如,如果想让老生代的大小是新生代的五倍,则设置参数为-XX:NewRatio=5,默认这个参数设定为2(即老生代占用堆空间的三分之二,新生代占用三分之一)。

  • 在启动JVM时,直接使用-Xmn参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。

  • 在启动JVM时,直接使用-XX:NewSize-XX:MaxNewSize参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。

(4)每一个线程都有一个栈,用于保存函数调用、返回地址等等,这些栈有着对应的内存分配。如果线程过多,就会导致OutOfMemoryError。即使你有足够的空间的堆来存放对象,你的应用也可能会因为创建一个新的线程而崩溃。这种情况下,需要考虑限制线程中的栈大小的最大值。线程栈大小可以在JVM启动的时候,通过-Xss参数来设置,默认这个值被设定为320KB至1024KB之间,这和平台相关。

性能监控

当开发或运行一个Java应用的时候,对JVM的性能进行监控是很重要的。配置JVM不是一次配置就万事大吉的,特别是你要应对的是Java服务器应用的情况。你必须持续的检查堆内存和非堆内存的分配和使用情况,线程数的创建情况和内存中加载的类的数据情况等。这些都是核心参数。

使用Anturis控制台,你可以为任何的硬件组件上运行的JVM配置监控(例如,在一台电脑上运行的一个Tomcat网页服务器)。

JVM监控可以使用以下衡量标准:

  • 总内存使用情况(MB):即JVM使用的总内存。如果JVM使用了所有可用内存,这项指标可以衡量底层操作系统的整体性能。

  • 堆内存使用(MB):即JVM为运行的Java应用所使用的对象分配的所有内存。不使用的对象通常会被垃圾回收器从堆中移除。所以,如果这个指数增大,表示你的应用没有把不使用的对象移除或者你需要更好的配置垃圾回收器的参数。

  • 非堆内存的使用(MB):即为方法区和代码缓存分配的所有内存。方法区是用于存储被加载的类的引用,如果这些引用没有被适当的清理,永生代池会在每次应用被重新部署的时候都会增大,导致非堆的内存泄露。这个指标也可能指示了线程创建的泄露。

  • 池内总内存(MB):即JVM所分配的所有变量内存池的内存和(即除了代码缓存区外的所有内存和)。这个指标能够让你明确你的应用在JVM过载前所能使用的总内存。

  • 线程:即所有有效线程数。举个例子,在Tomcat服务器中每个请求都是一个独立的线程来处理,所以这个衡量指标可以表示当前有多少个请求数,是否影响到了后台低权限的线程的运行。

  • 类:即所有被加载的类的总数。如果你的应用动态的创建很多类,这可能是服务器内存泄露的一个原因。

转载于:https://my.oschina.net/elain/blog/383104

Java虚拟机(JVM)(自动内存管理机制)相关推荐

  1. 一、JAVA虚拟机------JVM自动内存管理

    JVM自动内存管理 一.JAVA内存区与内存溢出 1.1 概述 1.2 运行时数据区 1.2.1 程序计数器 (Program Counter Register) 1.2.2 Java虚拟机栈(Jav ...

  2. 【深入理解Java虚拟机】自动内存管理机制——垃圾回收机制

      Java与C++之间有一堵有内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.C/C++程序员既拥有每一个对象的所有权,同时也担负着每一个对象生 ...

  3. java虚拟机的自动内存管理机制(二)

    1.内存分配: a.优先在新生代Eden区分配.Eden区没有足够的空间时,虚拟机发起一次Minor GC. (Major GC 是清理永久代.Minor GC 会清理年轻代的内存,Full GC 是 ...

  4. Java虚拟机JVM的内存管理

    Java虚拟机JVM的内存管理 关键词 一.JVM整体架构 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 名称 作用 特征 配置参数 异常 程序计数器 ...

  5. JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇<Java自动内存管理机制--Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

  6. 深入理解java虚拟机-1.自动内存管理

    文章目录 1.自动内存管理 1.1 Java内存区域与内存溢出异常 1.1.1 运行时数据区域 程序计数器 程序计数器为什么是私有的? java虚拟机栈 本地方法栈 虚拟机栈和本地方法栈为什么是私有的 ...

  7. 内存分段分页机制理解_深入理解虚拟机,JVM高级特性-自动内存管理机制

    什么是自动内存管理机制? 对于java程序员来说,有一点是要比C/C++程序员要方便的,那就是程序在运行时,java程序不需要为每一个对象其编写对应的释放内存的代码,JVM虚拟机将为你在合适的时间去释 ...

  8. 解析Java对象引用与JVM自动内存管理(2)

    解析Java对象引用与JVM自动内存管理(2) 作者:杨扬 本文选自:赛迪网 2002年11月22日 Soft References 应用实例 下面以在基于web的应用程序中使用soft refere ...

  9. JVM的内存管理机制详解

    一.为什么要学习内存管理? Java与C++之间有一堵由内存动态分配和垃圾回收机制所围成的高墙,墙外面的人想进去,墙里面的人出不来 对于Java程序员来说,JVM给我们提供了自动内存管理机制,不需要既 ...

  10. 第五篇:初识JVM,JVM自动内存管理

    文章目录 一.前言 1.1 计算机==>操作系统==>JVM 1.1.1 虚拟与实体(对上图的结构层次分析) 1.1.2 Java程序执行(对上图的箭头流程分析) 二.JVM内存空间与参数 ...

最新文章

  1. 电脑温度检测软件哪个好_一般电脑录音软件哪个好?
  2. oracle scott用户
  3. 在Paddle中利用AlexNet测试CIFAR10数据集合
  4. oracle客户端下载 win8.1,WINDOWS8.1安装ORACLE客户端及配置
  5. 开发日记-20190515 关键词 汇编语言(八) MVVM
  6. nuget pack
  7. 功能测试工具Selenium IDE
  8. leetcode刷题 162.寻找峰值
  9. SpringBoot2.0 整合 QuartJob ,实现定时器实时管理
  10. [原]MS SQL表字段自增相关的脚本
  11. 扫码枪收银有手续费吗_收银系统怎么选
  12. 利用python进行数据分析(4)
  13. DNS测试 nslookup
  14. Rust语言教程(1) - 一门没有GC的语言
  15. selenium如何执行网页脚本
  16. 瓶子机器人diy制作大全_塑料瓶回收改造成电动机器人玩具
  17. 牛津英语字典pdf下载_除了long time no see,你知道还有这些中式英语也进入了牛津字典吗...
  18. Vue学习之认识到应用(三)
  19. angularjs中使用百度统计
  20. 有情饮水饱下一句是什么?_百度知道

热门文章

  1. 排序算法-快速排序(入门)
  2. meanShift算法用于目标跟踪的优缺点
  3. FabFilter Total Bundle 2021 for Mac - 经典效果器合集(2022版)
  4. CentOS7 扩容时发现 /dev/mapper/centos-home 不存在,创建后登录终端显示 -bash-4.2
  5. Macbook 本机 apache 虚拟主机和网站,多域名、多虚拟目录,并且允许列举所有文件和目录
  6. 怎么用EasyRecovery把文件夹中丢失的数据恢复
  7. runtime之ivar内存布局篇
  8. Angular CLI 生成项目时css less scss样式使用说明
  9. 【SQLAlchemy】SQLAlchemy技术文档(中文版)(上)
  10. 技术支持诈骗手段翻新:借勒索软件类锁屏界面恐吓用户