JVM (一)

定义:Java Virtual Machine  --- java运行环境(Java二进制字节码的运行环境)

好处:

  • 一次编写 到处运行
  • 自动内存管理,垃圾回收功能
  • 数组下标越界检查
  • 多态 

比较:

jvm   jre    jdk 的区别

 jvm 只是运行环境 , 只是一个空壳 ,结合上基础类库(集合类,线程类,io类等)之后,才是真正意义上的java运行环境

在jre的基础上 结合上 编译工具(javac javap 等)就是java开发工具包

 内存结构

1、 程序计数器

2、 虚拟机栈

3、 本地方法栈

4、 堆 

5、 方法区

1、程序计数器 

定义:Pogram Counter Register 程序计数寄存器(寄存器)

作用:记住下一条 jvm 指令的执行地址。(计数器的物理实现是 CPU 中的寄存器)

特点:程序计数器是线程私有的,每一个线程都有属于自己的程序计数器,记录本线程jvm指令的执行位置 

 不会存在内存溢出

2、虚拟机栈 

2.1 定义 Java Virtual Machine Stacks(Java 虚拟机栈)

1 、每个线程运行时所需要的内存,称为虚拟机栈,多个线程同时运行可能会有多个虚拟机栈。

2、每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存。

3、每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法。(在栈顶正在执行的方法,就是活动栈帧)

 

问题辨析:

1) 垃圾回收是否涉及栈内存?

不涉及,因为栈帧内存在每一次方法执行完之后都会自动被弹出释放掉,不需要垃圾回收机制来进行回收。

2) 栈内存的分配越大越好吗?

栈内存可以通过运行代码时通过虚拟机参数指定。

-Xss size 

因为物理内存的大小是一定的,如果栈内存设置过大,会导致虚拟机栈的个数减数,即会导致线程数减少。 

500 x 1 = 250 x 2

栈内存大只是可以增加递归调用的次数,并不会提高程序的运行效率,反而由于线程数受到限制,可能还会降低程序的运行效率。一般采用系统默认的栈内存大小。

3) 方法内的局部变量是否线程安全?

看一个变量是否线程安全,就看该变量是多线程共享的,还是单个线程私有的。

1 如果方法内局部变量的作用范围没有逃离该方法,则该局部变量是线程安全的。 

2 如果该局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全。

3 如果该局部变量是基本类型的值,那么即是逃离了该方法的作用范围,该变量仍然是线程安全的。

 

/**
*局部变量的线程安全问题
*/
public class Test03 {
 public static void main(String[] args) {
 
 }
 
 public static void m1(){
 //线程方法内的局部变量,不会有线程安全问题
 StringBuilder sb = new StringBuilder();
 sb.append(1);
 sb.append(2);
 sb.append(3);
 System.out.println(sb);
 }
 
 public static void m2(StringBuilder sb){
 //不是线程安全的,StringBuilder 是作为方法参数传入的,
 // 可能有别的线程也可能访问到该 StringBuilder 对象
 sb.append(1);
 sb.append(2);
 sb.append(3);
 System.out.println(sb.toString());
 }
 
 public static StringBuilder m3(){
 //不是线程安全的,虽然 StringBuilder 是方法内的局部变量,但是作为返回值返回了
 //所以将来有可能别的线程会拿到该对象的引用,对该对象的值进行改变
 StringBuilder sb = new StringBuilder();
 sb.append(1);
 sb.append(2);
 sb.append(3);
 return sb;
 } }

判断某个变量是否是线程安全的,除了该变量得是方法中的局部变量,还得判断该变量是否逃离了该方法的作用范围。

2.2 栈内存溢出

栈帧过多导致栈内存溢出

方法递归调用时,没有设置正确的递归结束条件,导致无限递归

 

package pys;

/*
演示栈桢
 */
public class Demo_1 {
    private static int count;
    public static void main(String[] args) {
        try{
            method1();
        }catch (Throwable e){
            e.printStackTrace(); //java.lang.StackOverflowError :栈内存溢出
            System.out.println(count); //21025 //修改栈内存后:2080
        }
    }
    private static void method1(){
        count++;
        method1();
    }
}

栈帧过大导致栈内存溢出

第三方代码中出现两个类中循环引用的问题,导致无限递归。 

 2.3 线程运行诊断

案例 1:CPU 占用过多

        定位

  •  用 top 定位哪个进程对 cpu 的占用过高
  • ps H -eo pid,tid,%cpu | grep 进程 id(用 ps 命令进一步定位是哪个线程引起的 cpu 占用过高)
  • jstack 进程 id 可以根据线程 id 找到有问题的线程,进一步定位到问题代码的源码行数

案例 2:程序运行很长时间没有结果

        线程间的死锁问题:deadlock

3、本地方法栈(Native Method Stacks)(线程私有的)

本地方法:指那些不是由 java 代码编写的方法。

java 代码不能直接与操作系统的底层打交道,而是需要通过一些 c++语言所写的 API 来与底层进行交互。

本地方法栈:给本地方法的运行提供空间

4、堆(Heap) 

4.1 定义:通过 new 关键字,创建的对象都会使用堆内存。

4.2 特点:

        它是线程共享的,堆中对象都需要考虑线程安全问题

        堆内没有被引用的对象都会被垃圾回收机制回收

4.3 堆内存溢出

不断产生新的对象,且对象都被引用了,无法触发垃圾回收机制,长时间就会导致堆内存溢出。

-Xmx8m  设置堆内存大小

package pys;

import java.util.ArrayList;
import java.util.List;

public class Demo_1 {
    public static void main(String[] args) {
        int i = 0;
        try{
            List<String> list = new ArrayList<>();
            String a = "hello";
            while(true){
                list.add(a); //hello,hellohello,hellohellohellohello...
                a = a + a; //hellohellohellohello...
                i++;
                //在程序走到 catch 之前,list 对象都有被引用,不能被垃圾回收机制回收
                //同理,hello 这些字符串对象都被加入到 list 中,所以也被引用了,无法被回收。
            }
        }catch (Throwable e){
            e.printStackTrace(); //java.lang.OutOfMemoryError: Java heap space:堆内存溢出
            System.out.println(i); //25 //更改堆最大内存后,为 17
        }
    }
}

 4.4 堆内存诊断

1.jps 工具 查看当前系统中有哪些 java 进程

2.jmap 工具 

        查看堆内存占用情况:只能查看某一时刻的堆内存占用情况

        使用方式:jamp -heap 进程 id

3.jconsole 工具

        图形界面的,多功能的监测工具,可以连续监测

命令行讲解:

>jps 【jps 命令,查看当前系统中的 java 进程】

5920

7988 Test06

8212 Jps

3240 Launcher

9448 KotlinCompileDaemon

jmap -heap 7988 【jmap 命令:查看当前 7988 进程的堆内存占用情况,是该时刻的】

Attaching to process ID 7988, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.181-b13

using thread-local object allocation.

Parallel GC with 8 thread(s)

Heap Configuration: 【堆内存配置】

MinHeapFreeRatio = 0

MaxHeapFreeRatio = 100

MaxHeapSize = 2107637760 (2010.0MB) 【最大堆内存空间】

NewSize = 44040192 (42.0MB)

MaxNewSize = 702545920 (670.0MB)

OldSize = 88080384 (84.0MB)

NewRatio = 2

SurvivorRatio = 8

MetaspaceSize = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize = 17592186044415 MB

G1HeapRegionSize = 0 (0.0MB)

Heap Usage: 【第一个时间节点的堆内存使用情况,此时 byte 数组还未被创建】

PS Young Generation

Eden Space:

capacity = 33554432 (32.0MB) 【总容量】

used = 4705344 (4.48736572265625MB) 【已使用容量】

free = 28849088 (27.51263427734375MB)

14.023017883300781% used

From Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

To Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

PS Old Generation

capacity = 88080384 (84.0MB)

used = 0 (0.0MB)

free = 88080384 (84.0MB)

0.0% used

3179 interned Strings occupying 260568 bytes.

jmap -heap 7988

Attaching to process ID 7988, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.181-b13

using thread-local object allocation.

Parallel GC with 8 thread(s)

Heap Configuration:

MinHeapFreeRatio = 0

MaxHeapFreeRatio = 100

MaxHeapSize = 2107637760 (2010.0MB)

NewSize = 44040192 (42.0MB)

MaxNewSize = 702545920 (670.0MB)

OldSize = 88080384 (84.0MB)

NewRatio = 2

SurvivorRatio = 8

MetaspaceSize = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize = 17592186044415 MB

G1HeapRegionSize = 0 (0.0MB)

Heap Usage: 【第二个时间节点的堆内存使用情况,此时 byte 数组已被创建】

PS Young Generation

Eden Space:

capacity = 33554432 (32.0MB)

used = 15191120 (14.487380981445312MB) 【已使用容量增加了 10mb】

free = 18363312 (17.512619018554688MB)

45.2730655670166% used

From Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

To Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

PS Old Generation

capacity = 88080384 (84.0MB)

used = 0 (0.0MB)

free = 88080384 (84.0MB)

0.0% used

3180 interned Strings occupying 260616 bytes.

D:\JAVA\JVM\codeTest>jmap -heap 7988

Attaching to process ID 7988, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.181-b13

using thread-local object allocation.

Parallel GC with 8 thread(s)

Heap Configuration:

MinHeapFreeRatio = 0

MaxHeapFreeRatio = 100

MaxHeapSize = 2107637760 (2010.0MB) NewSize = 44040192 (42.0MB)

MaxNewSize = 702545920 (670.0MB)

OldSize = 88080384 (84.0MB)

NewRatio = 2

SurvivorRatio = 8

MetaspaceSize = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize = 17592186044415 MB

G1HeapRegionSize = 0 (0.0MB)

Heap Usage: 【第三个时间节点的堆内存使用情况,此时 byte 数组已被垃圾回收机制回收】

PS Young Generation

Eden Space:

capacity = 33554432 (32.0MB)

used = 671112 (0.6400222778320312MB)

free = 32883320 (31.35997772216797MB)

2.0000696182250977% used

From Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

To Space:

capacity = 5242880 (5.0MB)

used = 0 (0.0MB)

free = 5242880 (5.0MB)

0.0% used

PS Old Generation

capacity = 88080384 (84.0MB)

used = 1009328 (0.9625701904296875MB)

free = 87071056 (83.03742980957031MB)

1.1459168933686756% used

3166 interned Strings occupying 259632 bytes.

jconsole:图形界面

案例: 垃圾回收后,内存占用仍然很高

排查步骤:

step1:先使用 jps 命令查看当前正在运行的进程

step2:使用 jmap -heap 进程 id 来查看当前进程的堆内存使用情况

        关注新生代内存占用:Eden Space

        关注老年代内存占用:PS Old Generation

step3:使用 jconsole 工具连接到当前的进程上,在内存选项卡中,点击执行(GC(G))按钮

 step4:再次使用 jmap 进行查看

介绍一种新工具:jvisualvm 可视化的虚拟机

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值