JVM系统自学笔记1

一、介绍

定义:java virtual meachine -java运行时环境(java二进制字节码的运行环境)。

好处:

  1. 一次编写到处运行
  2. 自动内存管理,垃圾回收
  3. 数组下标越界检查
  4. 多态

比较:

常见的JVM

java虚拟机管理的内存包括如下几个区域:

二、程序计数器(线程私有)

1.什么是程序计数器?

程序计数器是当前线程正在执行的字节码的地址。程序计数器是线程隔离的,每一个线程在工作的时候都有一个独立的计数器。

2.字节码的执行原理

编译后的字节码在没有经过JIT(实时编译器)编译前,是通过字节码解释器j进行解释执行。其执行原理为:字节码解释器读取内存中的字节码,按照顺序读取字节码指令,读取一个指令就将其翻译成固定的操作,根据这些操作进行分支,循环,跳转等动作。

3.程序计数器的作用?

从字节码的执行原理来看,单线程的情况下程序计数器是可有可无的。因为即使没有程序计数器的情况下,程序会按照指令顺序执行下去,即使遇到了分支跳转这样的流程也会按照跳转到指定的指令处继续顺序执行下去,是完全能够保证执行顺序的。

但是现实中程序往往是多线程协作完成任务的。JVM的多线程是通过CPU时间片轮转来实现的,某个线程在执行的过程中可能会因为时间片耗尽而挂起。当它再次获取时间片时,需要从挂起的地方继续执行。在JVM中,通过程序计数器来记录程序的字节码执行位置。程序计数器具有线程隔离性,每个线程拥有自己的程序计数器

4.程序计数器的特点

(1)程序计数器具有线程隔离性

(2)程序计数器占用的内存空间非常小,可以忽略不计

(3)程序计数器是java虚拟机规范中唯一一个没有规定任何OutofMemeryError的区域

(4)程序执行的时候,程序计数器是有值的,其记录的是程序正在执行的字节码的地址

(5)执行native本地方法时,程序计数器的值为空。原因是native方法是java通过jni调用本地C/C++库来实现,非java字节码实现,所以无法统计

三、虚拟机栈

1、定义: Java Virtual Mechine Stacks(java虚拟机栈)

  • 每个线程运行时所需要的内存空间
  • 每个栈由多个栈帧组成,对应着该线程调用方法占用的内存
  • 每个线程都只有一个活动栈帧,对应着线程当前执行的方法

2、问题:

  • 垃圾回收是否涉及到栈内存?
    答: 不会涉及。因为栈对应着一个程序的执行过程,在执行过程中,活动栈不断随着该方法的运行结束而弹出,直到程序执行完成,栈为空,消失。即一个进程执行完毕,线程栈消失。
  • 栈内存越大越好吗?
    答: 不是。服务器物理内存是固定的,若栈内存越大,则能够同时运行的线程数量就会减小。栈是由多个栈帧组成,内存增大,只是将可以容纳栈帧的数量增多,除了可以调用更多的方法(递归),没有其他的作用。-Xss设置栈内存大小,一般-Xss=1M
  • 线程的局部变量是否线程安全?
    答: 不一定。方法内的局部变量且没有逃离方法的作用访问时,是线程安全的。如果局部变量引用了对象,由于对象存在于堆中,一般其他线程可以访问修改,需要考虑线程安全。

3、栈内存溢出

  • 栈帧过多,栈被撑破了(递归)
  • 栈帧过大(交叉引用)

例如:

package com.example.jvm;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.Arrays;
import java.util.List;

public class StackOverFlow {

    public static void main(String[] args) throws JsonProcessingException {
        Dept dept = new Dept();
        dept.setName("jndx");
        Emp e1 = new Emp();
        Emp e2 = new Emp();
        e1.setName("zx");
        e2.setName("hkw");
        e1.setDept(dept);
        e2.setDept(dept);
        dept.setEmp(Arrays.asList(e1,e2));
        ObjectMapper mapper = new ObjectMapper();
	//输出对象的Json字符串
        System.out.println(mapper.writeValueAsString(dept));
    }



}
class Dept{
    private String name;
    private List<Emp> emp;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Emp> getEmp() {
        return emp;
    }
    public void setEmp(List<Emp> emp) {
        this.emp = emp;
    }



}
class Emp{
    private String name;
    private Dept dept;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }

}

结果:

原因:

在输出dept时,dept内有emp,emp内又有dept。

四、线程诊断步骤

1、定位进程,top找出占用cpu过多的进程PID

2、ps -H -eo pid,tip,%cpu | grep PID 找出该进程下占用过大的线程 TID

3、jstack pid 查看进程中各线程的详细信息,可以看出问题所在。注意:这里TID为十六进制,TID转换为16进制。

五、本地方法栈(线程私有)

本地方法栈类似于虚拟机栈,也是线程私有。

不同点:本地方法栈服务的对象是jvm运行的native方法,而虚拟机栈服务的是jvm执行的java方法。调用的是c/c++语言编写的方法。

六、java堆

1、定义: Heap,通过new关键字创建的对象,都存放在堆内存中

2、特点

  • 线程共享,堆中的对象都存在线程安全的问题
  • 垃圾回收,垃圾回收机制重点区域。

七、堆内存诊断

1、工具:

  • jps
    • 查看系统有哪些进程。
  • jmap
    • 查看堆内存该时间使用情况 jmap -heap PID
  • jconsole
    • 图形界面,多功能检测工具,连续监测。
  • jvistualvm
    • 通过“dump”查找占用内存代码位置

2、例子

public class Demo1 {
    public static  void main(String[] args) throws InterruptedException {
        System.out.println("1....");
        Thread.sleep(30000);
        byte[] array =  new byte[1024 * 1024 * 10];//10M
        System.out.println("2....");
        Thread.sleep(30000);
        array = null;//array没有引用,可以被回收了
        System.gc();
        System.out.println("3...");
        Thread.sleep(1000000L);
    }
}

运行上面代码,首先得到Demo进程PID,后每次输出后,jmap -heap PID得到如下内容

D:\openSourceProject\jvm1>jps
8916 Launcher
9876 RemoteMavenServer36
11656
13976 Demo1
13756 Jps

D:\openSourceProject\jvm1>jmap -heap 13976
Attaching to process ID 13976, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4276092928 (4078.0MB)
   NewSize                  = 89128960 (85.0MB)
   MaxNewSize               = 1425014784 (1359.0MB)
   OldSize                  = 179306496 (171.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 67108864 (64.0MB)
   used     = 6711104 (6.40020751953125MB)
   free     = 60397760 (57.59979248046875MB)
   10.000324249267578% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 179306496 (171.0MB)
   used     = 0 (0.0MB)
   free     = 179306496 (171.0MB)
   0.0% used

3175 interned Strings occupying 260400 bytes.

D:\openSourceProject\jvm1>jmap -heap 13976
Attaching to process ID 13976, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4276092928 (4078.0MB)
   NewSize                  = 89128960 (85.0MB)
   MaxNewSize               = 1425014784 (1359.0MB)
   OldSize                  = 179306496 (171.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 67108864 (64.0MB)
   used     = 17196880 (16.400222778320312MB)
   free     = 49911984 (47.59977722167969MB)
   25.62534809112549% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 179306496 (171.0MB)
   used     = 0 (0.0MB)
   free     = 179306496 (171.0MB)
   0.0% used

3176 interned Strings occupying 260456 bytes.

D:\openSourceProject\jvm1>jmap -heap 13976
Attaching to process ID 13976, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.221-b11

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 4276092928 (4078.0MB)
   NewSize                  = 89128960 (85.0MB)
   MaxNewSize               = 1425014784 (1359.0MB)
   OldSize                  = 179306496 (171.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 67108864 (64.0MB)
   used     = 1342200 (1.2800216674804688MB)
   free     = 65766664 (62.71997833251953MB)
   2.0000338554382324% used
From Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
To Space:
   capacity = 11010048 (10.5MB)
   used     = 0 (0.0MB)
   free     = 11010048 (10.5MB)
   0.0% used
PS Old Generation
   capacity = 179306496 (171.0MB)
   used     = 1106008 (1.0547714233398438MB)
   free     = 178200488 (169.94522857666016MB)
   0.6168253937659905% used

3162 interned Strings occupying 259464 bytes.

jconsole使用 控制台输入jconsole

八、方法区

1、定义: 其中主要存储class文件的信息(包括类信息和class文件常量池)和运行时常量池。

jdk6(永久代实现)和jdk8(元空间实现)中方法区的区别,其中最主要的区别是8中将方法区转移到本地内存中,且常量池分为运行时常量池和字符串常量池;且字符串常量池被留在内存中的堆中。

2、内存溢出

永久代jdk1.6

元空间jdk1.8

发布了171 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/QilanAllen/article/details/105559478
今日推荐