Java Virtual Machine: the Essential Guide--译文

译文地址:https://anturis.com/blog/java-virtual-machine-the-essential-guide/

介绍:

  Java虚拟机(JVM)是Java应用程序的执行环境。在一般的意义上来说,JVM是一种抽象的计算机,通常,Java虚拟机是指用严格的指令集以及全面的内存模型来具体实施本规范的。它也可以指的是软件实现的运行时实例。主要的参考实现的JVM是HotSpot。

  JVM规范确保任何实现能够以完全相同的方式来解释字节码。它可以被实现为一个过程,一个独立的Java操作系统,或者直接执行字节码的处理器芯片。最广为人知的JVM是作为流行的平台(Windows,Mac OS X,Linux上,Solaris等)的进程中运行的软件实现。

  JVM的架构能够详细控制Java应用程序执行的操作。它运行在沙箱环境,并确保应用程序没有访问本地文件系统,进程和网络没有适当的权限。如果远程执行的,代码应该使用证书进行签名。

  除了解释Java字节码,JVM的大多数软件实现包括刚刚在实时(JIT)编译器生成机器代码为经常使用的方法。机代码是CPU的本机语言,并且可以执行比解释字节码快得多。
  你并不需要了解Java虚拟机是如何工作的发展和运行Java应用程序。但是,如果你对多性能问题有一定的了解,你将避免很多问题。

架构:

JVM规范定义了子系统和它们的外在行为。 Java虚拟机具有以下主要的子系统:

Class Loader:负责读取Java源代码和加载类到数据区域。

Execution Engine:负责从数据区域执行指令。

数据区占据了由JVM从底层操作系统分配的内存。

Class Loader

JVM使用分为以下几个层次不同的类加载器:

(1)引导类加载器是父加载器为其他类加载器。它加载了核心Java库,并且是唯一一个用本机代码。

(2)扩展类加载器是引导类加载器的一个孩子。它加载的扩展库。

(3)系统类加载器是扩展类加载器的一个孩子。它加载被发现在classpath中,是应用程序的类文件。

(4)用户定义的类加载器是系统类加载器或其它用户定义的类加载器的一个孩子。

  当一个类加载器接收到一个请求加载一个类时,它会检查缓存,看看这个类已经被加载,然后委托请求父。如果父无法加载的类,那么孩子尝试加载类本身。子类加载器可以检查父类加载器的缓存,但父不能看到孩子装入的类。这种设计,因为一个孩子的类加载器不应该被允许加载已装入由它的父类。

Execution Engine

执行引擎从装载到数据区域逐个字节码执行命令。使字节码指令变成可读的机器码,所述的执行引擎使用方法有两种

Interpretation:当遇到它时,执行引擎会改变每个命令为机器语言

Just-in-time (JIT) compilation:如果一个方法被被经常使用,执行引擎将其编译为本机代码,并将其存储在缓存中。在此之后,使用该方法相关联的所有命令都没有直接解释执行。

  虽然JIT编译时间比解释的时间多,它是一种方法,可能会被调用数千次只进行一次。运行这样的方法作为本机代码比每次遇到一个命令节省了大量的时间。

  JIT编译不是JVM规范的要求,它不是用来提高JVM性能的唯一方法。该规范只定义了其中的字节码指令涉及的本机代码;它是由实现去定义所述的执行引擎怎样执行这种转换。

Memory Model

Java内存模型是建立在自动内存管理的概念。当一个对象不再被应用程序引用时,垃圾收集器会丢弃它,它会释放内存。这是与很多其他编程语言,你必须手动卸载内存中的对象不同的。
JVM对底层操作系统分配内存,将其分为以下几个方面。

Heap Space:这是用来存放对象的共享内存区域,一个垃圾收集扫描器

Method Area:这个地区以前被称为持久代,其中装载的类分别存放近来已经从JVM和类除去的资源

Native Area:这一领域是基本类型和引用变量

  将堆分成几代确保高效的内存管理,因为垃圾收集器并不需要扫描整个堆。大多数对象居住在很短的时间,以及那些存活时间较长可能不需要被丢弃直到应用程序终止
  当一个Java应用程序创建一个对象,它是存储在堆中的伊甸园池。一旦满了,一个小垃圾收集被触发在伊甸园池。首先,垃圾收集器标记死的对象(那些不被应用程序的任何多个被引用),并递增活对象的时间(时间是由该对象已存活垃圾回收的数量来表示)然后垃圾收集器会丢弃死的对象和移动活的对象生活的幸存者池,离开伊甸园池。
  当一个幸存的对象达到了一定的时间,就被移动到了老一代堆:将终身池。最终,终身池填满和主要的垃圾收集被触发把它清理干净
  当进行垃圾收集,所有应用程序线程被停止,从而导致停顿。小型垃圾回收频繁,但经过优化,能够迅速去除死的对象,这是年轻一代的重要组成部分。这比主要的垃圾收集慢得多,因为它们涉及到时间长的对象。有各种不同的垃圾收集器中,执行的主要垃圾收集时,有些收集器可能会在某些情况下更快。
  堆的大小是动态的。存储器被分配给仅当它是必需的堆当堆填满时,JVM重新分配更多的内存,直到达到最大值内存重新分配也导致应用程序简单地停下来。

Threads

  在JVM运行在一个单一进程,但它可以同时执行多个线程,每一个都运行其自己的方法这就是Java的一个重要部分如即时通讯客户端的应用程序,运行在至少两个线程;一个等待用户输入,一个用于检查服务器收到的消息另一个例子是,在执行不同的线程请求的服务器应用程序:有时每个请求可以包括同时运行多个线程
  所有线程共享内存,并提供给JVM进程的其他资源。每个JVM进程开始于入口点的主线程(main()方法)其他线程是从它开始,目前执行的独立路径线程可以并行地在不同的处理器上运行,也可以共享一个处理器线程调度器控制线程如何执行在单个处理器上。

Performance Optimization

  JVM的性能取决于它是如何以及配置相匹配的应用程序的功能虽然内存使用的垃圾收集和重新分配内存的进程自动管理,但你必须控制他们的频率,一般情况下,你的应用内存需求的越多,你内存管理用的就少,这样就会停止你的应用

  如果垃圾收集正在发生的频率比你想的快,你就可以使用更大的堆,它需要整整一代堆的用更长的时间来填补,就会有越少的垃圾收集发生,配置最大堆的大小,使用Xmx 选项,当你开启JVM默认情况下,将最大将最大堆大小设置为可用的物理存储器的操作系统的任一1/4,或为1 GB(取最小)

  如果问题是重新分配内存,可以设置初始堆大小是一样的最大值。这意味着,JVM将永远需要分配更多的内存堆不过,你也将失去动态的堆大小所获得的自适应内存优化堆将是目前的固定大小的,你启动应用程序要配置初始堆大小,使用-Xms选项,当您启动JVM默认情况下,初始堆尺寸设置为可用的物理存储器的操作系统的任一1/64,或一些合理的最低是的不同的平台(取最大)

  如果你知道哪些垃圾收集(或大或小),可能会造成性能下降,你可以设置老少两代人之间的比例,而不改变整体的堆大小,对于应用那将创造更多的短命的对象,增加年轻一代的大小(那将留下给老对象的内存)对于使用长了不少幸存的对象进行操作的应用程序,增加了老一代的大小(通过为年轻一代的设置更少的内存)通过以下方法可以用来控制老少世代的内存大小

(1)当您启动JVM NewRatio选项:指定老少一代使用-XX之间的比率例如,为了使老一代比年轻一代的五倍,指定某某:NewRatio=5默认情况下,该比率设置为2(老一代占堆⅔,而年轻一代占据⅓)

(2)指定年轻一代使用,当你启动JVM的-Xmn选项的初始和最大大小老一代的尺寸将被设置任何记忆仍保留在堆中

(3)分别指定了年轻一代的初始和最大大小,使用XX:NewSize和某某:当你开始MaxNewSize选项在JVM老一代的尺寸将被设置任何记忆留在堆

  大多数应用程序(尤其是服务器)需要并发执行,处理多项任务有些任务是在给定时刻更重要的,有些则是可每当CPU是不是忙着做别的执行后台任务任务在不同的线程中执行。例如,一台服务器可能有一个低优先级的线程计算基于一些数据统计和启动更高优先级的线程来处理传入的数据,而另一个高优先级的线程服务请求的一些计算得出的数据。可以存在的数据从服务器请求数据许多来源,和许多客户机。每个请求都将短暂停止的后台计算线程的执行服务请求所以,你必须监控正在运行的线程数量,并确保有足够的CPU时间来制定必要的计算线程

  每个线程有一个栈,保存的方法调用,返回地址,等等一些内存分配的堆栈,如果有太多的线程,这可能会导致内存溢出的错误。即使你有分配给的对象足够的堆内存,应用程序可能无法启动一个新线程在这种情况下,考虑限制堆栈的最大大小的线程。要配置的线程堆栈大小,使用-Xss选项,当您启动JVM默认情况下,线程堆栈大小设置为320 KB或1024 KB,这取决于平台

Performance Monitoring

  无论您是开发或正在运行的Java应用程序,它监视JVM的性能是非常重要的配置JVM是不是一次性的事情,尤其是如果你正在处理的Java上运行的服务器你必须不断地检查这两个堆和非堆内存,该应用程序创建的线程的数目,并且被加载到内存中的类的数量的分配和使用这些核心参数

  使用Anturis控制台,你可以设置监控JVM的任何硬件组件(如运行的Tomcat Web服务器的计算机)中加入Java虚拟机监视器组件的基础设施。

Java虚拟机监视器可以测量以下指标:

(1)总的内存使用量(MB)是在JVM使用的内存量。如果JVM会消耗所有可用内存,该指标会影响底层操作系统的整体性能

(2)堆内存使用量(MB)的内存是在JVM分配的使用正在运行的Java应用程序对象的数量。未使用的对象是在堆由垃圾回收器定期清除如果这个指标增长,它可能表明给你的应用程序是不是移除未使用的的对象引用,或者你需要正确配置垃圾收集器

(3)非堆内存使用量(MB)的内存分配方法区和代码缓存量。方法区用于存储引用加载的类。如果这些参考文献没有适当除去持久代池,可以增加每个应用程序被重新部署的时间,导致了非堆内存泄漏。它也可以表示一个线程的创建泄漏。

(4)总池内存使用量(MB)是由JVM分配的各种内存池的内存(也就是总的内存,而不代码缓存区),这可以给你一个你的应用程序使用内存的情况

(5)线程(线程)是JVM中的活动线程的数目。举例来说,每个请求到Tomcat服务器用一个单独的线程来处理,所以这个指标可以给你当前正在处理的请求的数目的概念,以及它是否会影响设置为线程运行后台任务较低的优先级。

(6)(类)加载类的数量。如果您的应用程序动态创建了大量的类,这可能是一个严重的内存泄漏源

完了,写到这不知不觉感觉自己翻译的太烂了,大家还是读原文吧!

原文地址:https://www.cnblogs.com/sxmcACM/p/4019383.html