1.JVM的位置

        JVM称为java虚拟机,本质上就是一个运行在OS上的程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令
        Java的可移植性正是建立在JVM的基础上。任何平台只要装有针对于该平台的Java虚拟机,字节码文件(.class)就可以在该平台上运行。这就是“一次编译,多次运行”。
        
        Java运行环境(Java Runtime Environment,简称JRE)是一个软件,可以让操作系统运行Java应用程序(Java Application)。JRE的内部有一个JVM以及一些标准的类别函数库(Class Library)。

2.JVM的体系结构

        Java虚拟机包括一套字节码指令集(程序计数器)、一组寄存器、一个虚拟机栈、一个本地方法栈、一个堆和一个方法区。
        

(1)类加载器(Class Loader)

        类加载器只负责类的加载,不管是否可以运转。
  • 类加载器的分类:一共有以下4种类加载器。

    • 启动类加载器(BootStrapClassLoader,又叫根加载器)
              加载 jre/lib 包下面的 jar 文件,比如说常见的 rt.jar
    • 扩展类加载器(ExtClassLoader)
              加载 jre/lib/ext 包下面的 jar 文件。
              
    • 应用程序类加载器(AppClassLoader)
              根据程序的类路径(classpath)来加载 Java 类。
    • 用户自定义加载器(CustomClassLoader)
              继承 java.lang.ClassLoader 类。
  • new一个实例的过程?

  • 如何获取类加载器?
    比如我们定义了一个Student类,并创建了一个student实例,现在要获取这个类的类加载器:

(2)native

  • 在Thread的start()方法的源码中,我们可以看到如下的start0()这个方法,他用native关键字修饰了,那么这个native关键字是什么呢?

    ——被native关键字修饰,说明java的作用范围达不到了,即java实现不了了,它就会去调用底层的c语言。
  • 被native修饰的方法是怎么调用底层c语言的呢?
    在JVM中有一个本地方法栈(Native Method Stack),被native修饰的方***进入本地方法栈,然后通过调用本地方法接口(Java Native Interface,JNI)来调用本地方法库(比如c++的库、py的库)中的方法来实现java实现不了的功能

(3)方法区

        方法区是堆的一个★逻辑★部分 因此为了与堆内存区分开来,方法区也被叫做“非堆”1.8以前的永久代和1.8以后的元空间都是方法区的一种实现。    
        方法区被所有线程共享。所有定义的方法的信息都保存在方法区,此区域属于共享区。
        静态变量(static)、(final)、(Class)信息(构造方法、接口定义)、运行时的常量池都存在方法区中,但是new出来的实例变量存在堆内存中,实例引用在栈中,和方法区无关。

(4)虚拟机栈(Stack)

        虚拟机栈的栈元素是栈帧,当有一个方法被调用时,代表这个方法的栈帧入栈; 当这个方法返回时,其栈帧出栈。栈帧存储了方法的局部变量表、操作数栈、动态连接、方法返回地址等信息。
        虚拟机栈里可以放8大基本数据类型对象引用(如下面new出来的实例test1,会指向堆中保存的具体实例)、实例的方法(就是上面的方法)。
        方法结束就出栈,相当于垃圾回收了,所以虚拟机栈不存在垃圾回收机制
        虚拟机栈是线程私有的,栈的生命周期与线程相同,一旦线程结束,栈也就释放了。
        栈满溢出错误——StackOverflowError。

(5)★堆(Heap)

        JVM的堆内存只有一个,因此是所有线程共享的。堆内存的大小可以调节
        类加载器加载了类文件后,一般会把类的实例对象成员方法成员变量放在堆内存。
  • 堆内存的划分
    • 新生区(Young Generation):存放新生成的对象。新生区又分为伊甸园区和两个幸存者区,Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
      • 伊甸园区(Eden):所有对象都是在伊甸园区new出来的
      • 两个幸存者区——幸存0区、幸存1区:是伊甸园区与老年区过渡的部分。
    • 老年区(Old Generation):存储长期存活的对象。从新生区活下来就到了老年区。大对象(需要大量连续内存空间的对象,如数组、字符串)直接进入老年区,主要是为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。
    • 永久区(Permanent Generation):永久存储区是一个常驻内存区域,用来存储java运行环境所必须的类信息永久区不存在垃圾回收,随JVM的关闭而释放。到了JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是元空间并不在JVM中,而是使用本地内存
  • ★垃圾回收(GC)机制
            由于堆存放了大量引用类型的实例,这些实例可能被用过一次以后就不在用了,慢慢地就成了垃圾,所以堆中要有
    垃圾回收机制。垃圾回收分为轻GC(Minor GC)和重GC(Full GC)。
  • 为什么会产生OOM异常?
            
  • 对于伊甸园区来说,太小,会频繁minor gc,太大,一次gc的时间就太长了;对于幸存者区来说,太小,对象会过早的进入老年区,更容易触发full gc,太大会浪费内存,因为幸存者区会有一半长时间用不到。

(6)栈、堆、方法区的交互关系

        
        假设我们有一个test类,实例化test的过程是怎么样的?
        test类先被加载到方法区,然后在方法区中会有一个该类的模板以及对应的常量池(存放类的成员变量)。new一个test实例的时候,会在堆中开辟一块内存空间,用来存放test1实例对象,堆中实例中放的是取成员变量和成员方法的地址。然后再栈中存放对象引用,该引用指向堆中的test实例。
        

3.双亲委派机制

(1)为什么需要双亲委派机制?(双亲委派机制解决了什么问题?)

        类加载器的加载流程是:
  • 当AppClassLoader加载一个class时,它不会自己先去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成;
  • 当ExtClassLoader加载一个class时,它也不会自己先去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成;
  • 如果BootStrapClassLoader加载成功,加载流程结束;如果加载失败,会再往下使用ExtClassLoader来尝试加载;
  • 如果ExtClassLoader加载成功,加载流程结束;如果加载失败,会再往下使用AppClassLoader来尝试加载;
  • 如果AppClassLoader加载成功,加载流程结束;如果也加载失败,则会报ClassNotFoundException异常
        这种层次关系被称作为双亲委派模型,即如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。
        使用双亲委派机制好处就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,可以保证 Java 程序的稳定运作,可以防止对恶意的外层同名类的加载。
举例】比如我们知道java.lang.String类,里面有个toString()方法。我们也可以自己创建一个同名类——java.lang.String,也写一个toString(),然后整个main方法进行运行,但是运行起来会报错说没有main方法。明明我们写了main方法,为什么会说没有main方法呢?这是因为双亲委派机制一层一层往上委派到启动类加载器,启动类加载器发现没有main方法,所以会报这个错。
        

4.沙箱安全机制(了解)

        沙箱是一个限制程序运行的环境(沙箱主要限制系统资源的访问,如cpu、内存等。不同级别的沙箱对这些资源的访问限制也不一样)
        沙箱安全机制就是将java代码限定在JVM特定的运行范围中,并且严格限制代码对本地系统资源的访问,通过这样的措施来保证对代码的有效隔离,防止对系统造成破坏。

5.Jprofiler9.2.1



6.GC(垃圾回收)机制

        JVM会自动进行垃圾回收,垃圾回收只作用于堆内存和方法区,不包括虚拟机栈。
        GC主要针对于堆内存的伊甸园区、幸存者区和老年区。

(1)分类

        GC一般分为三种:
  • Minor GC:发生在新生代的GC,发生Minor GC时用户线程暂停时间比较短
  • Major GC:发生在老年代的GC。
  • Full GC:全局GC,针对新生代和老年代的GC,暂停时间长。

(2)垃圾搜集(标记、定位)算法

        我们怎么知道哪些对象是垃圾,需要对他们回收呢?——垃圾搜集算法,也称为垃圾定位、垃圾标记。
        常见的垃圾搜集算法有:引用计数法可达性分析算法
  • 引用计数法:每一个对象都会有一个引用计数器,每有一个引用指向它时,引用计数器就会加1,这样只需要判断某个对象的引用计数器是不是0,是0说明没有引用指向该对象,它就成了垃圾,就该对其回收了。
    缺点:如果两个对象都是垃圾,但是他俩相互引用,这样引用计数器永远不为0,就不能把他俩回收了。
  • 可达性分析算法:从内存根对象(GC Root)出发,看看有哪些对象是可以到达的,无法到达的就意味着无法访问,自然就是垃圾了。
            可以作为根对象(一定不会被GC的对象)的有:
    • 虚拟机中引用的对象
    • 静态属性引用的对象
    • 常量引用的对象
    • Native方法引用的对象

(3)三色标记法和并发漏标问题

        知道哪些对象是垃圾对象后该怎么标记呢?——采用三色(黑灰白)标记法。
  • 黑色:已标记;
  • 灰色:标记中;
  • 白色:未标记,垃圾对象。
        并发漏标问题:并发漏标问题指的是在标记时,用户线程仍能工作,所以可能在进行标记时,用户线程将对象间的引用关系给改了,导致漏标存活对象,被当成垃圾(浮动垃圾)的问题。
      【如何解决并发漏标问题?
  • 增量更新:只要赋值发生了,被赋值的对象就会被记录下来,然后会暂停用户线程,对这些被记录的对象再进行一遍标记(重新标记)
  • 原始快照(SATB)新加的对象(被引用的对象)会被记录下来,被删除引用关系的对象也会被记录下来。

(4)★GC的常用算法

        常用的有标记-清除算法、标记-复制算法、标记-整理算法、分代收集算法
  • 标记-清除算法:通过两次遍历内存区域,第一次遍历标记要回收的对象,第二次遍历对标记对象进行回收。
    优点:
    缺点:
            ①需要两次遍历,效率差
            ②容易产生大量内部碎片
  • 标记-复制算法:将内存划分为两部分,每次只用二者其中的一部分内存。当一部分内存区域满了触发GC的时候,就将存活对象复制到另一块内存区域,然后清除刚才用的那部分内存区域。如果再满了就重复刚才的过程。
    优点:
            ①效率高,只需要记住内存的首尾地址,一次性清除就行了;
            ②不会产生内部碎片
    缺点:
            ①内存利用率不高,每次只能使用一半内存;
            ②如果存活对象比较多,复制时也会比较耗时。
  • 标记-整理算法标记-复制算法适合对新生代(存活时间短)进行回收,不适用于老年代,因为老年代中对象存活率较高,一个对象可能被复制过来复制过去的。标记整理算法是对需要回收的进行标记,然后把存活对象移动到内存的一端,然后清理掉要回收的对象。
  • ★分代收集(回收)算法:分代收集算法就是根据对象的存活时间长短,把内存分为新生代和老年代。新生代对象存活时间短,每次垃圾回收可能只有少量存活(复制成本低),因此可以选择标记复制算法。老年代对象存活时间长,因此不适合用标记复制,可以选择标记清楚或标记整理算法。
    • 新生代采用标记复制算法:新生代的内存划分为伊甸园区幸存者区,幸存者区又分为两块——from(0区)和to(1区)。新创建的对象都在伊甸园区,当内存不够时会触发GC(Minor GC),就把伊甸园区存活的对象复制to区(谁空谁是to),然后给这些对象的年龄+1,然后对伊甸园区进行回收,并将from和to进行互换。当再触发GC时,就把伊甸园区和from区的存活对象复制到to区,然后进行垃圾回收。当一个对象的年龄超过15(默认是15),就会进入老年代。
      【tips】jvm对新生代的默认比例是8:1:1。根据IBM公司对象存活时间的统计研究,发现 80% 的对象存活时间都很短,所以他们将 Eden 区设置为新生代总内存的 80%,这样可以减少内存空间的浪费,提高内存空间利用率
    • 空间分配担保:如果新生代有大量存活对象要进入老年代,老年代空间也不够了怎么办?——老年代的空间分配担保。在每次对新生代GC前(Minor GC),jvm会先检查老年代可用内存是否大于新生代所有对象总大小(防止极端情况下新生代对象全存活下来进入老年代),如果大于,就可以对新生代GC了;如果小于,会根据是否允许担保失败(-XX:-HandlePromotionFailure),如果说允许失败,就会判断老年代内存是否大于之前每次Minor GC后进入老年代的对象平均大小,如果大于,就jvm就会觉得老年代空间够,就可以进行对新生代GC;如果说小于之前每次GC进入老年代对象的平均大小,或者不允许失败,就会触发Full GC,对老年代进行垃圾回收,再执行Minor GC对新生代回收。

(5)★常见的垃圾回收器

        常见的垃圾回收器有:
  • 串行回收器:Serial、Serial Old(Serial老年代的版本)
            Serial 串行回收器意味着在进行GC时,其他线程要阻塞等待,直到GC结束
  • 并行(parallel )回收器:Parallel Scavenge(PS)、Parallel Old(PO)
            并行回收器就是可以在同一时刻多条GC线程一起工作。
            并行GC在伊甸园区内存不足时会采用标记复制进行Minor GC,在老年代内存不足时会采用标记整理进行Full GC,这两个GC过程都会暂停用户线程(STW),暂停时间较长。
            并行回收器适用于注重吞吐量(比如计算多)的场景,因为会暂停较长时间的用户线程,所以不适用于注重响应时的场景。
  • ★★并发回收器CMS(Concurrent Mark Sweep)、G1(Garbage First)。并发回收器使得可以GC和用户线程在一段时间同时工作

(6)G1垃圾回收器

        1)Region分区
                G1回收器的内存模型不是采用传统的将内存从物理上划分为新生代和老年代,而是将堆内存划分为大小相等的内存块Region,将新生代和老年代也划分成块用Region来表示,可以通过动态分配的方式实现新生代和老年代各自在逻辑上的连续
                
                H区是专门用来存放大对象的,如果一个对象的大小超过Region的一半,就被认为是大对象。在其他垃圾回收器中,大对象都是直接放在老年代中,但是如果大对象存活时间很短,就会频繁触发老年代GC;G1为了解决这个问题就用H区来存放大对象,如果一个region放不下,就找连续region(上图的HHH)来放,实在找不到才会触发full gc。
        2)RSet(Remember Set)
                在进行可达性分析时,其他回收器需要进行整堆扫描,G1为了避免整堆扫描,给每个Region分配了一个RSet,它记录了其他Region对该Region的引用(反向指针记录),这样就不需要整堆扫描了,只需要看一下RSet里记录的引用自己的对象是否存活即可。
        3)CardTable(卡表)
              【为什么引入?
                如果一个线程更改了一个Region的引用,就需要通知RSet更改引用记录,如果引用对象很多的话,就需要对每个引用进行处理,比较麻烦。G1为了解决这个问题,引入了CardTable。
                Region被划分为若干个Card,每个card都用一个字节来标记是否被修改过,cardtable就记录了每个card的修改情况,当对一个对象引用进行写操作(即改变对象引用)时,会产生写屏障(write barrier)将card标记为脏卡
        【tips】Card就是堆内存中的最小可用单位,也就是说给对象分配内存分配的是连续的Card,因此每次回收时也是对Card进行处理。
        4)写屏障
                在对对象引用执行写操作时,会产生写屏障。写屏障通过 SATB+RSet 解决了并发漏标的问题
  • 写前屏障:在执行赋值(=)语句时(即,将=左边的对象引用修改到=右边),=左边之前引用的对象所在region会减少一个引用,jvm就会在赋值语句生效前记录减少的引用对象。此时不会立刻更新RSet,而是通过原始快照的方式批量处理
  • 写后屏障:当执行赋值语句后,=右边的对象获取了左边对象的引用,右边对象所在分区的RSet也要更新(同样采取原始快照的方式)。
        5)CSet(Collect Set)
                CSet就是要回收的region的集合。CSet里的region可以分为两种,对应于G1的两种GC模式:
  • young GC:young GC时,CSet里的region只包含新生代region;
  • Mixed GC:Mixed GC时,CSet里包含所有新生代region和部分老年代region。
        6)停顿时间
                我们对jvm进行调优的目的,其实就是为了减少Minor GC和Full GC带来的停顿时间,而G1可以直接设置期望停顿时间(-XX:MaxGCPauseMillis),在期望的停顿时间内,对一部分 Region进行垃圾回收。
              【如何实现的?】
                G1 会收集每个 Region 的回收耗时、垃圾占比等信息,然后计算回收每个 Region 带来的收益大小,也就是说可以回收多少内存,要花多久回收,通过维护一个优先级列表来记录回收每个Region的收益,这样在设置的最大停顿时间内,G1就会回收那些能带来最大收益的 Region。
              【停顿时间越短越好吗?】
                不是的。停顿时间太短可能导致每次GC只能回收很少的region,最终导致Full GC,就得不偿失了。

(7)G1的优缺点?

  • 优点
    • G1采用分区的方式,将内存分为若干个region,新生代和老年代不需要连续存放,同时又可以实现逻辑上的连续。而且G1还划分了H区,专门用来存放大对象,不是像其他回收器那样大对象直接放进老年代,这样可以避免大对象存活时间短,频繁触发老年代GC的情况;
    • 从整体上看,G1采用标记整理回收;从局部上看,G1采用标记复制回收,这两种回收都不会产生内部碎片;
    • G1可以指定最大停顿时间,按照回收收益进行回收。
  • 缺点:在用户程序运行时,G1 无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额外执行负载(Overload)都要比 CMS 要高。从经验上来说,在小内存应用上 CMS 的表现大概率会优于G1,而 G1 在大内存应用上则发挥其优势。平衡点在 6-8GB 之间。
    • 内存占用高:G1中需要给每个Region分配一个RSet来记录引用情况,所以G1占用的内存比CMS高;
    • 执行负载高:G1 和 CMS 都是用到了写屏障来维护RSet,不同的是,CMS 使用了写后屏障来维护RSet,而 G1 不仅用了写前屏障还用了写后屏障,写前屏障用来跟踪并发时的指针变化,从而实现 SATB(原始快照),使用写后屏障来维护RSet中的卡表。由于 G1 对写屏障的复杂操作比 CMS 会消耗更多的资源,因此在 CMS 中,直接使用同步操作来实现写屏障,而在 G1 中不得不使用类似于队列的数据结构来实现写前屏障和写后屏障,进行异步处理。

(8)什么时候会触发GC?

        jvm主要有三种GC,它们的触发条件也不相同:
  • Minor GC:当伊甸园区满了的时候,会触发Minor GC(from区满了不会触发);
  • Major GC:在老年代空间不足时,会先尝试触发Minor GC,如果空间还不够,就会触发Major GC;
  • Full GC:
    • ①主动调用System.gc()时,系统会建议执行Full GC,但是jvm不一定会真的执行;
    • 没有指定新生代和老年代大小,随着程序运行可能会出发Full GC;(用-Xms、-Xmx设置
    • 老年代空间不足,比如说大对象直接进入老年代,但是老年代装不下该对象了;
    • 空间分配担保失败时,也就是说Minor GC后进入老年代的对象的平均大小大于老年代可用内存
    • ⑤在1.8之前,方法区用永久代来实现的时候,方法区满了也会触发Full GC。(配置采用CMS GC)

(9)什么时候会进入老年代?

  • 当新生代的存活对象经历15次GC后还没被回收时(被认为是长时间存活的有价值的对象),就会进入老年代;
  • 幸存者区内存不足的时候,存活对象也会进入老年代;
  • 一些大对象被创建后会直接进入老年代,因为大对象在新生代拷贝来拷贝去太耗时了。

(10)★介绍一下cms?

        cms是一种针对老年区并发垃圾回收器,采用标记清除算法,它允许应用程序在gc的同时继续执行,注重响应时间。使用CMS进行GC时有四个阶段,分别是:初始标记、并发标记、重新标记、并发清除
  • 初始标记:是记录被根对象(gc root)直接引用的对象会触发短暂的stw
  • 并发标记:从根对象直接引用的对象开始依次扫描进行标记,这个过程用户线程和gc可以同时进行,不会产生stw,但用户线程可能会产生浮动垃圾
  • 重新标记:针对并发标记中产生的浮动垃圾进行标记,会触发stw
  • 并发清除:正式开始清除垃圾,用户线程和gc线程可以同时进行,同样地用户线程会产生垃圾,但是这些垃圾这次gc就清除不了了。
        从CMS的四个阶段可以发现,cms不是完全并发的,有的阶段可以并发,有的阶段需要短暂的stw,比如初始标记、重新标记

(11)CMS的优缺点

  • 优点:并发收集、低停顿。
  • 缺点
    • ①采用标记清除算法,会产生内部碎片,这样可用的连续内存空间就少了,在给大对象分配内存时可能分配失败;
    • ②CMS无法处理并发清除时用户线程产生的浮动垃圾,只能在下一次GC时清除
    • ③★CMS比较消耗CPU资源,在并发阶段会导致用户线程变慢,降低用户线程的吞吐量

(12)为什么CMS采用标记清除算法不用其他的?

        如果采用标记复制和标记整理的话,都需要移动对象,这就导致需要更长时间的stw,这与cms追求的快速响应的初衷不相符;而且cms是并发的,在gc时用户线程也会执行,移动对象的内存地址可能会让用户线程出现问题。

7.★★★jvm调优相关知识

        

(1)什么时候需要调优(jvm性能指标)?

        一般是根据几个jvm的性能指标进行判断。对于一台服务器来说,每分钟的gc耗时(jvm.gc.time)在1s以内、每次minor gc耗时(jvm.gc.meantime)在100ms以内、full gc的次数(jvm.fullgc.count)最多几小时一次,一天不到1次最好、每次full gc耗时(jvm.fullgc.time)在1s以内,通常这几个指标正常就不需要调优,如果说某个指标出问题了,就需要考虑进行一些优化了。

(2)怎么进行jvm调优?

        对jvm进行调优的一般思路:
  • ①可以先参考系统运行日志、堆栈错误信息、gc日志、线程快照、dump文件(堆转储快照)等初步分析是出现了什么问题,比如出现了OOMCPU占用过高或者频繁的GC等问题;
  • ②然后针对问题用一些命令进行定位,并修改我们的代码;
  • 最后才去考虑调整jvm的参数,比如是不是新生代配置的太小了、堆内存配置的太小了,进行相应参数的修改:
    • -Xms和-Xmx一般设成相等(原因:空闲堆内存小于40%时,JVM会扩大堆到-Xmx指定的大小;空闲堆内存大于70%时,JVM会减小堆到-Xms指定的大小。如果在Full GC后满足不了内存需求会动态调整,这个阶段比较耗费资源);
    • 新生代尽量设置大一些,让对象在新生代多存活一段时间,每次Minor GC 都要尽可能多的收集垃圾对象,延迟对象进入老年代的时机,减少发生Full GC的频率。
  • 然后通过对比调优前后的差距,不断重复这个过程直到找到一个最佳的jvm参数设置。
  • ★OOM问题定位
    1)可以开启打印gc日志以及发生OOM时自动生成dump文件
        -XX:+PrintGCDetails:开启 GC 日志创建更详细的 GC 日志;
        ★-XX:+HeapDumpOnOutOfMemoryError:如果出现OOM问题,自动生成dump文件;
    2)当出现了OOM后,可以通过查看gc日志来分析young gc、full gc发生的频率以及每次gc的耗时,如果说频繁的发生gc,我们考虑是不是堆内存分配的不合理,可以调整 -Xms 和 -Xmx 参数重新分配堆内存;
                jvm -v:查看启动时jvm配置的参数
          然后可以借用一些工具软件来看dump文件(比如说mat),找出哪些对象占用了大量内存,然后修改代码来解决OOM问题。
  • ★cpu占用过高问题
    1)先用 top 查看各进程的cpu使用情况,它会按照cpu由高到低进行排列,然后找到占用最高的那个进程的id;
    2)然后通过 top -Hp pid 查看该进程下各个线程的cpu使用情况,找到占用最高的线程的id;
            -Hp:显示某进程下的线程,一般就是这样Hp组合使用。
    3)再用 jstack pid 拿到这个线程的堆栈信息进行分析,比如说是不是哪里出现死循环了等问题导致的cpu占用过高。

(3)有哪些调优的命令、参数?(你了解哪些jdk自带的常用调优工具?)

        1)命令
  • jps:用来查看jvm中运行的进程状态信息
            使用"jps -v"还可以查看设置的jvm参数,比如说我们设置的初始堆大小、最大堆大小等信息。
  • jstack:用来查看jvm中的线程堆栈信息,通过线程堆栈信息可以分析死锁死循环锁竞争等问题。
            如何用jstack排查死锁:先用jsp查看正在运行的进程的pid,然后通过jstack -l pid查看线程堆栈信息,来分析死锁。
  • jstat用来对jvm进行监控统计
            jstat -gc pid:查看指定进程的内存使用情况和gc情况。
        2)参数
  • –Xms 和 –Xmx:设置堆内存大小的初始值和最大值(一般为了避免堆内存频繁震荡导致系统性能下降,会设置最大堆等于最小堆);
  • -Xmn:设置新生代占整个堆的比例,默认是堆的3/8;
  • -Xss:设置每个线程的堆栈大小,如果这个值太小,可能导致栈溢出;如果设置的太大,会影响创建线程的数量上限;
  • -XX:PermSize 和 -XX:MaxPermSize:设置永久代大小(一般是整个堆的1/3)和永久代最大值;
  • -XX:NewRatio:设置新生代和老年代的占比;
  • -XX:SurvivorRatio:设置伊甸园区和幸存者区的占比;
  • -XX:MaxTenuringThreshold:设置对象进入老年代的年龄阈值。