• JVM介绍及参数调优


    一、JVM简单结构图

    java堆栈方法区概念和联系:
    解决的是数据存储的问题,即数据怎么放、放在哪儿。几乎所有的java 对象实例都存放在 java 堆中。堆空间是所有线程共享的,这是一块与 java 应用密切相关的内存空间。
    解决程序的运行问题,即程序如何执行,或者说如何处理数据。每一个 java 虚拟机线程都有一个私有的 java 栈,一个线程的 java 栈在线程创建的时候被创建,java 栈中保存着帧信息,java 栈中保存着局部变量、方法参数,同时和 java 方法的调用、返回密切相关。
    方法区则是辅助堆栈的一块永久区(Perm),解决堆栈信息的产生,是先决条件。
    我们创建一个新的对象,User: 那么User类的一些信息(除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字符串字面量和数字常量)而User类被实例化出来之后,被存储到java堆中,一块内存空间。当我们去使用的时候,都是使用User对象的引用,形如User user = new User();这里的user就是存放在java栈中的,即User真实对象的一个引用。

    辨清java堆:

    java堆是和java应用程序关系最密切的内存空间,几乎所有的对象都存放在其中,并且java堆完全是自动化管理的,通过垃圾回收机制,垃圾对象会自动清理,不需要显示地释放。
    根据垃圾回收机制不同,Java堆有可能拥有不同的结构。最为常见的就是将整个java堆分为新生代老年代。其中新生代存放新生的对象或者年龄不大的对象,老年代则存放老年对象。
    新生代分为eden区、s0区、s1区,s0和s1也被称为from和to区域,他们是两块大小相等并且可以互换角色的空间。
    绝大多数情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,则会进入s0或者s1区,之后每经过一次新生代回收,如果对象存活则它的年龄就加1 ,当对象达到一定的年龄后,则进入老年代。
     
    类加载子系统: 
     类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称为方法区的内存空间。
     
    直接内存:
     java 的 NIO 库允许 java 程序使用直接内存。直接内存是在 java 堆外的、直接向系统申请的内存空间。通常访问直接内存的速度会优于 java 堆。因此出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。
     
    垃圾回收系统:
    垃圾回收系统是 java 虚拟机的重要组成部分,垃圾回收器可以对方法区、java 堆和直接内存进行回收。其中,java 堆是垃圾收集器的工作重点。和 C/C++不同,java 中所有的对象空间释放都是隐式的,也就是说,java 中没有类似 free()或者 delete()这样的函数释放指定的内存区域。对于不再使用的垃圾对象,垃圾回收系统会在后台默默工作,默默查找、标识并释放垃圾对象,完成包括 java 堆、方法区和直接内存中的全自动化管理。
     
    本地方法栈:
    本地方法栈和 java 栈非常类似,最大的不同在于 java 栈用于方法的调用,而本地方法栈则用于本地方法的调用,作为对 java 虚拟机的重要扩展,java 虚拟机允许 java 直接调用本地方法(通常使用 C 编写)
     
    二、参数配置
     
    堆分配参数:

    堆溢出处理:

     

    栈配置:

    方法区:

     

     三、垃圾收集算法(Garbage Collection,简称GC)

    谈到垃圾回收(Garbage Collection,简称GC) , 需要先澄清什么是垃圾,类比日常生活中的垃圾,我们会把他们丢入垃圾桶,然后倒掉。GC中的垃圾,特指存于内存中、不会再被使用的对象,而回收就是相当于把垃圾“倒掉”。垃圾回收有很多种算法:如引用计数法标记压缩法复制算法分代分区的思想。

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

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

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

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

    分代算法:就是根据对象的特点把内存分成N块,而后根据每个内存的特点使用不同的算法。对于新生代和老年代来说,新生代回收频率很高,但是每次回收耗时都很短,而老年代回收频率较低,但是耗时会相对较长,所以应该尽量减少老年代的GC.

    分区算法:其主要就是将整个内存分为N多个小的独立空间,每个小空间都可以独立使用,这样细粒度的控制- -次回收都少个小空间和那些个小空间,而不是对整个空间进行GC,从而提升性能,并减少GC的停顿时间。

    对象如何进入老年代:

    一般而言对象首次创建会被放置在新生代的eden区,如果没有GC介入,则对象不会离开eden区,那么eden区的对象如何进入老年代呢?一般来讲,只要对象的年龄达到一定的大小,就会自动离开年轻代进入老年代,对象年龄是由对象经历数次GC决定的,在新生代每次GC之后如果对象没有被回收则年龄加1。虚拟机提供了一个参数来控制新生代对象的最大年龄,当超过这个年龄范围就会晋升老年代。
    -XX:Max Tenuring Threshold,默认情况下为15。
    另外,大对象(新生代eden区无法装入时,也会直接进入老年代)。JVM里有个参数可以设置对象的大小超过在指定的大小之后,直接晋升老年代。
    -XX:PretenureSize Threshold

    总结:使用PretenureSize Threshold可以进行指定进入老年代的对象大小,但是要注意TLAB区域优先分配空间。 

    TLAB全称是Thread Local Allocation Buffer即线程本地分配缓存,从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生的。每一个线程都会产生一个TLAB,该线程独享的工作区域,java虚拟机使用这种TLAB区来避免多线程冲突问题,提高了对象分配的效率。TLAB空间一般不会太大,当大对象无法在TLAB分配时,则会直接分配到堆上。-XX:+UseTLAB使用TLAB
    -XX:+TLABSize设置TLAB大小
    -XX:TLABRefillWasteFraction设置维护进入TLAB空间的单个对象大小,他是一个比例值,默认为64,即如果对象大于整个空间的1/64,则在堆创建对象。
    -XX:+PrintTLAB查看TLAB信息
    -XX:ResizeTLAB自调整TLABRefillWasteFraction阀值。

     

    对象创建流程图:

    一个对象创建在什么位置,我们的jvm会有一个比较细节的流程,根据数据的大小参数的设置,决定如何分配创建,以及其位置。
     
  • 相关阅读:
    因为付出,所以喜欢。开发就是这么坑!
    停滞在一个圈子,决定人生的高低![深度文章]
    我不曾忘记的初心-程序员如何看待买房子
    能力要进化-还在技术停滞不前吃老本吗?
    能力要进化-还在技术停滞不前吃老本吗?
    我不曾忘记初心-我们最终都成了自己讨厌的人
    我不曾忘记初心-我们最终都成了自己讨厌的人
    我不曾忘记的初心-冒险努力正是你缺少的!
    我不曾忘记的初心-冒险努力正是你缺少的!
    JS之正则表达式
  • 原文地址:https://www.cnblogs.com/girl1314/p/11023446.html
Copyright © 2020-2023  润新知