JVM内存管理

JVM图解

在这里插入图片描述

  • 程序计数器

概念:可以看做当前线程所执行的字节码的行号指示器。

特点:线程私有的内存

  • Java 虚拟机栈

概念:描述的是 java方法执行的内存模型。(每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程。)

特点 : 线程私有,生命周期和线程 相同 。 这个区域会出 现 两种 异 常 :StackOverflowError 异常: 若线程请求的深度大于虚拟机所允许的深度 。OutOfMemoryError 异常:若虚拟机可以动态扩展,如果扩展是无法申请到足够的内存。

  • 本地方法栈

概念:它与虚拟机栈所发挥的作用是相似的,区别是 java 虚拟机栈为执行 java 方法服务,而本地方法栈是为本地方法服务。

特点:线程私有,也会抛出两类异常:StackOverflowError 和OutOfMemoryError。

  • 堆区

概念:是被所有线程共享的一块区域,在虚拟机启动时创建。

特点:线程共享,存放的是对象实例(所有的对象实例和数组),GC
管理的主要区域。可以处于物理上不连续的内存空间。

  • 方法区

概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。 特点:线程共享的区域,抛出异常 OutOfMemory

异常:当方法区无法满足内存分配需求的时候。


双亲委派机制

Bootstrap classLoader(根加载器)

主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。

ExtClassLoader(拓展加载器)

主要负责加载jre/lib/ext目录下的一些扩展的jar。

AppClassLoader(App加载器)

主要负责加载应用程序的主函数类

  • 1.类加载器收到类加载的请求
  • 2.将这个请求向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
  • 3.启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则,抛出异常,通知子加载器进行加载
  • 4.重复步骤3

其实就是为了安全,防止了黑客可以通过篡改系统级别的类:如java.lang.String,JVM会优先通过根加载器去加载系统的String类,就不会去加载自己编写的String类了


沙箱安全机制

Java安全模型的核心就是Java沙箱(sandbox),沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。


Native关键字

凡是带了native 关键字的,说明java的作用范围达不到了,会调用底层C语言的库!进入本地方法栈,调用本地方法本地接口。


栈(stack)

  • 先进后出,这也是为什么main方法一开始最早执行最后结束。方法执行完后出栈,局部变量就会被回收,JVM不存在这一块的调优

基本类型的变量数据和对象的引用都是放在栈里面的


堆(heap)

我们new的对象实际都存在了堆里面的伊甸园区,满了就经过GC的清洗,幸存下来就进入幸存区,再满了又进行GC清洗,又幸存下来的就进到老年区,变量引用指针存在于java虚拟机栈中,去存地址指向堆里面的对象信息。

堆内存满了会报OOM异常

解决方法:
1.把堆内存变大
2.还报错就代码是不是进死循环了

和栈的区别

Java的内存分为两类,一类是栈内存,一类是堆内存。栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个栈中的变量也将随之释放。堆是与栈作用不同的内存,一般用于存放不放在当前方法栈中的那些数据,例如,使用new创建的对象都放在堆里,所以,它不会随方法的结束而消失。方法中的局部变量使用final修饰后,放在堆中,而不是栈中。

对象本身放在堆里面,显式的String常量放在常量池,String对象放在堆中。

字符串常量池

这也是在堆中

public class Test {

    public static void main(String[] args) {
        String s1 = "hello"; // 在常量池
        String s11 = "hello"; // 没有建立对象,直接从常量池取
        String s111 = "hell" + "o"; // 编译优化,不会创建对象,而是优化成了 s111 = "hello"进行处理,这样可直接从常量池取
        String s2 = new String("hello"); // new在堆中
        String s3 = s2; // 栈中的值,也就是引用地址相同
        System.out.println("s1 == s11--> " + (s1 == s11)); // true,要加一个括号,不然先进行字符串的拼接然后在比较,就一定没false了,没有达到我们想要的效果
        System.out.println("s1 == s111--> " + (s1 == s111));// true
        System.out.println(s1 == s2); // false
        System.out.println(s1 == s3); // false
        System.out.println(s2 == s3); // true
        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.equals(s3)); // true
        System.out.println(s2.equals(s3)); // true
    }

}
public static void main(String[] args) {
    String s1 = "hello"; // 在字符串常量池创建对象
    /**
     * @Description: 先在字符串常量池检测有没有"hello",没有则在字符串常量池创建一个对象,
     *               然后在堆中再创建一个String对象,引用指向常量池的对象,然后将堆的String对象地址再给s2引用
     *               就可能会创建一个或两个对象,关键在于字符串常量池有没有已经存在的。
    */
    String s2 = new String("hello");
}

JVM调优

其实就是针对两个区(堆区和方法区)调

常用的调优参数

配置参数功能
-Xms初始堆大小。如:-Xms256m
-Xmx最大堆大小。如:-Xmx512m
-Xmn新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor,即 90%

就是合理去调整程序的堆区各部分的大小,不让频繁进行GC垃圾回收。不然又使用到的对象,又得new 出来,影响JVM的效率


GC回收

什么叫垃圾对象

在这里插入图片描述

怎么判别是垃圾对象

  • 可达性分析法

将“GC Roots”对象作为起点,从这些节点开始向下搜索引用的对象,找到的对象都标记为非垃圾对象,其余未标记的对象都是垃圾对象
GC Roots根节点:线程栈的本地变量、静态变量、本地方法栈的变量等等

  • 选择java虚拟机栈变量作为GC Roots进行图解
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值