学习JVM---入门

这篇具有很好参考价值的文章主要介绍了学习JVM---入门。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.JVM体系结构

JVM的位置

学习JVM---入门

JVM体系结构

学习JVM---入门

2.类加载器

双亲委派机制

package java.lang;

/**
 * 测试自定义java.lang.String类能否运行成功
 * 体会双亲委派机制
 *
 * 类加载器逐级向上检查:app->ext->boot
 * 发现boot类加载器中也有String类,但是没有main方法,于是报错
 * app:应用程序加载器
 * ext:扩展类加载器
 * boot:启动类(根)加载器
 *
 * 检查什么?每一级类加载器能够加载的类是固定的,不能越级加载。
 * boot能加载的类,app,ext就不能加载;同理,exit能加载的,app就不能加载。
 * 一个形象的比喻:类,app,ext,boot分别对应平民,村长,乡长,县长。
 * 村长会向乡长汇报,乡长向县长汇报。如果这个案子很特殊,应该有县长来处理,那么村长和乡长当然不能管了。
 *
 * 通过验证“hello”是否会输出,可以知道:先加载类,再去执行main方法。
 * 类是方法的载体,包括main方法,想要用方法,就得先加载类
 */
public class String {
    public static void main(String[] args) {
        System.out.println("hello");        //这一行会输出吗?
        String s = "";
        System.out.println(s.getClass().getClassLoader());
    }
}

运行结果:

错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
   public static void main(String[] args)
否则 JavaFX 应用程序类必须扩展javafx.application.Application
public class Cat {
    /**
     * 测试自定义普通类使用哪个类加载器
     *
     * 从app到boot检查是否有Cat类,发现ext和boot中都没有Cat类
     * 所有直接还是用app加载器
     */
    public static void main(String[] args) {
        System.out.println("Hello");
        System.out.println(Cat.class.getClassLoader());     //运行结果:sun.misc.Launcher$AppClassLoader@18b4aac2
    }
}

3.沙箱安全机制

4.native

native是Java的一个关键字,使用它可以调用本地方法,访问本地资源。

Thread类中的一个用法:

private native void start0();

执行到start0()时,start0()进入本地方法栈,然后调用本地方法接口JNI(Java Native Interface),然后调用本地方法库。

5.方法区

存哪些东西

  • static变量
  • final变量
  • Class对象
  • 常量池

实例变量存放在堆中。

面试题:一个实例的创建过程?

  1. 类加载,Class对象存放在方法区中。
    1. 父类静态成员
    2. 子类静态成员
    3. 父类代码块
    4. 父类构造器
    5. 子类代码块
    6. 子类构造器
  2. 实例的声明 Object obj,存放在栈中。
  3. 实例的创建 new Object(),存放在堆中。
  4. 将对象实例的地址赋给对象的引用(栈中的变量名指向堆中具体的对象)。obj = new Object();
  5. 对对象的属性赋值。
  6. 调用方法。
    学习JVM---入门

6.栈

特点:先进后出,后进先出

为什么main方法先执行,后结束?

学习JVM---入门

进栈顺序:main(),test1(),test2()

出栈顺序:test2(),test1(),main()

为什么递归会引起栈溢出?

学习JVM---入门

当调用递归出现死循环的情况时,栈溢出也就出现了。

测试代码:

package stack_;

public class TestStack {
    public static void main(String[] args) {
        test1();
    }
    static void test1(){
        test2();
    }

    static void test2(){
        test1();
    }
}

运行结果:

Exception in thread "main" java.lang.StackOverflowError
	at stack_.TestStack.test2(TestStack.java:12)
	at stack_.TestStack.test1(TestStack.java:8)
	……
	此处省略1000多行(马德,typora软件都干卡死了)
	……
	at stack_.TestStack.test2(TestStack.java:12)
	at stack_.TestStack.test1(TestStack.java:8)

Process finished with exit code 1

7.堆

堆:heap

堆中的三个区域

  • 新生区
    • 伊甸园
    • 幸存区0
    • 幸存区1
  • 养老区
  • 永久区

新生区

学习JVM---入门

伊甸园:类的创建,应用,甚至消亡。

伊甸园满了之后,触发GC,有一部分被销毁,有一部分进入幸存区。幸存区0,1又会发生数据交换。

老年区

新生区满了之后,触发Full GC,进入老年区。

老年区和新生区都满了,就会发生OOM。

一般不会出现OOM,因为99%的数据都是临时的,用完就不再使用,在伊甸园或幸存区就被回收了。

永久区

JDK1.6及以前:永久代,常量池位于方法区

JDK1.7:永久代,常量池位于堆

JDK1.8及以后,永久区更名为:元空间,常量池位于元空间

内存调优

//虚拟机需要的最大内存
        long max = Runtime.getRuntime().maxMemory();
        //虚拟机初始化时的总内存
        long original = Runtime.getRuntime().totalMemory();

        System.out.println("max:" + max + "Byte," + max / 1024 / 1024 + "MB");
        System.out.println("original:" + original + "Byte," + original / 1024 / 1024 + "MB");
/**
 * 调优之前:
 * max / 电脑内存 ≈ 1 / 4
 * original / 电脑内存 ≈ 1 / 64
 
 *内存调优:-Xms1024m -Xmx1024m -XX:+PrintGCDetails
 */

运行结果:

max:1029177344Byte,981MB
original:1029177344Byte,981MB
Heap
 PSYoungGen      total 305664K, used 20971K
  eden space 262144K, 8% used
  from space 43520K, 0% used
  to   space 43520K, 0% used
 ParOldGen       total 699392K, used 0K
  object space 699392K, 0% used
 Metaspace       used 3282K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 356K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

(305664K+699392K)/ 1024 = 981M

元空间的内存逻辑上存在,物理上不存在。

OOM 内存溢出

案例演示

package heap_;

import java.util.Random;

/**
 * 测试堆内存溢出
 */
public class TestOOM {
    public static void main(String[] args) {
        String s = "hello";
        while (true){
            s +=  s + s + new Random().nextInt(999999999);
        }
    }
}

运行结果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at java.util.Arrays.copyOf(Arrays.java:3332)
	at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
	at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
	at java.lang.StringBuilder.append(StringBuilder.java:136)
	at heap_.TestOOM.main(TestOOM.java:9)

Process finished with exit code 1

Java中的字符串可以不停的增加长度,但是JVM中的堆内存空间是有限的。

对虚拟机参数进行调整(-Xms8m -Xmx8m -XX:+PrintGCDetails),并观察运行结果。

可以看到很快就会运行结束,并报OOM错误。

出现OOM,如何解决?

  • 内存调大一点
  • 如果还是有问题,就要研究代码,是否有bug

使用Jprofiler分析内存

Jprofiler安装教程:https://www.cnblogs.com/zhangxl1016/articles/16220183.html

测试代码:

package heap_;

import java.util.ArrayList;

public class TestDump {
    byte[] arr = new byte[1024 * 1024];     //共1MB的空间

    public static void main(String[] args) {
        ArrayList<TestDump> list = new ArrayList<>();
        int count = 0;
        try {
            while (true) {
                list.add(new TestDump());
                count++;
            }
        } 
        //OOM要用Error来捕获
        catch (Error error) {
            System.out.println("count=" + count);
            error.printStackTrace();
        }

    }
}

运行结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3996.hprof ...
Heap dump file created [7778880 bytes in 0.024 secs]
count=6
java.lang.OutOfMemoryError: Java heap space
	at heap_.TestDump.<init>(TestDump.java:6)
	at heap_.TestDump.main(TestDump.java:13)

Process finished with exit code 0

并在当前项目的根目录下生成了Jprofiler文件:
学习JVM---入门

双击打开:
学习JVM---入门

可以看到最上边的列表项(ArrayList)占用的内存是最高的,说明是它出了问题。

那么具体是代码中的哪一行有问题呢?
学习JVM---入门

因为这个案例只有main方法一个线程,所以直接点main,然后在下方可以看到是源代码第13行出了问题。

为什么到count=6时就报错了呢?因为在第13行每加一个对象,就会增加1MB的空间,我们分配的最大空间是8MB,所以加到6的时候,就会发生内存溢出。

记得删除生成的Jprofiler文件,因为比较占用空间。
学习JVM---入门

虚拟机参数

-Xms 设置初始化内存大小 默认1/64

-Xmx 设置最大分配内存 默认1/4

-XX:PrintGCDetails 打印GC垃圾回收信息

-XX:HeapDumpOnOutOfMemoryError 转储OOM异常信息

上一个案例设置为:-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
学习JVM---入门

学习JVM---入门

垃圾回收

哪些东西是垃圾?

不需要的,而且占了空间的对象。

在哪里回收?

为什么不在栈回收?因为栈里没有垃圾,栈不存对象,只存储变量的引用和方法。

举个例子:假设堆中存的是具体的人,那么栈中存的是人的姓名。人去世之后,这个具体的对象依然存在堆中,而且占用空间,所以它需要被回收。栈里存的名字,用完了就自动出栈了,所以栈中不存在垃圾,亦无需回收。

如何回收?

引用计数法

学习JVM---入门

每一个对象都有一个计数器,某个对象被使用一次,相应的计数器就会加1。垃圾回收时,计数器为0的对象被回收。

缺点:当对象很多时,计数器也会占用大量的资源。

复制算法

主要针对新生区

假设当前伊甸园有两个对象:o1,o2,GC之后,o1被销毁,o2进入幸存区。幸存区有两个,要去哪个呢?哪个是空的,就去哪个。另外一个幸存区中,如果有对象,也会进入to区。然后当前的to区又变成from区,from区又变成to区。一定要保证有一个幸存区是空的,这个区就是to区。

”谁空谁是to“

学习JVM---入门

一个对象在新生区经历15次(默认次数)还没有消亡,那么它会进入老年区,类似于久经沙场的老兵,活到最后就可以养老了。

这里涉及到一个JVM参数:-XX:MaxTenuringThreshold=15,默认值是15,如果这个值调到很大,那么新生区的对象会很难进入到老年区中。

  • 优点:没有内存碎片。
  • 缺点:浪费空间。
    • 总是要保证to区是空的。
    • 假设对象100%存活,to区就要面临无法容纳所有对象的情况。to区同时要面临伊甸园和form区两个方向的对象。
  • 最佳使用场景:对象存活度较低的区域---新生区。
标记压缩清除算法

第一次扫描:标记存活的对象,没被标记的就是不需要的

第二次扫描:清除没用的对象,会产生碎片

最后一次扫描:被标记的对象向一侧移动,另一侧就是被清除掉的

学习JVM---入门

优点:不会增加额外的空间

缺点:扫描会浪费时间,会有内存碎片产生

优化:先进行几次标记清除,最后再统一压缩

算法比较
  1. 内存效率:复制算法>标记清除>标记压缩(时间复杂度)

    • 复制是一个动作,清除是两个动作,压缩有三个动作
  2. 内存整齐度:复制算法=标记压缩>标记清除

    • 复制和压缩都没有内存碎片
  3. 内存利用率:标记清除=标记压缩>复制

    • 清除和压缩都在原有空间中操作,复制算法总是要保证to区是空的

有没有最优算法呢?

没有,只有最合适的:分代收集算法

新生代:存活率低,适合复制算法。不用担心to区的空间不够。

老年代:区域大,存活率高,适合标记清除+标记压缩混合实现。碎片不是很多的时候用标记清除,碎片积累到一定程度,就需要压缩,这其中就要涉及到内存调优。文章来源地址https://www.toymoban.com/news/detail-711886.html

到了这里,关于学习JVM---入门的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux学习】初始冯诺漫体系结构

    什么是冯诺依曼体系结构? 冯诺依曼体系结构是一种将程序指令和数据以二进制形式存放在主存储器中,由中央处理器统一控制和执行的计算机系统结构。冯诺依曼体系结构实现了程序的可编程性和硬件与软件的分离,促进了计算机的发展。冯诺依曼体系结构由五个基本部件

    2024年04月23日
    浏览(41)
  • STM32&ARM体系结构(嵌入式学习)

    STM32是意法半导体(STMicroelectronics)公司推出的一系列32位ARM Cortex-M微控制器(MCU)产品系列。它们基于ARM架构,并且具有广泛的应用领域,包括工业自动化、消费电子、医疗设备、通信、汽车电子等。 STM32系列提供了多个产品系列,以满足不同应用需求和性能要求。其中常见

    2024年02月08日
    浏览(62)
  • 【从零开始学习JAVA | 第二十三篇】集合体系结构

    目录 前言: 单列集合:      set与list的区别: 双列集合: map的特点: 总结:                   JAVA中为我们提供了很多集合,这些集合都有自己很独特的特点,因此我们要学习所有的集合,但是在学习所有的集合之前,我们还是先为大家介绍一下JAVA的集合体系结构,这

    2024年02月16日
    浏览(55)
  • 学习体系结构 - Arm 通用中断控制器 v3 和 v4

    Learn the architecture - Arm Generic Interrupt Controller v3 and v4 Version 3.2 借助DeepL翻译 + 个人补充一些内容 建议提前阅读: arm 的 异常模型 本指南概述了 Arm 通用中断控制器 (GIC) v3 和 v4 的功能,并介绍了兼容 GICv3 的中断控制器的操作。它还介绍了如何配置 GICv3 中断控制器以便在裸机环

    2024年01月16日
    浏览(60)
  • 计算机体系结构之CPU的构建和性能优化(个人学习)

    计算机体系结构是计算机科学中一门关键的领域,而其中的中央处理单元(CPU)更是整个计算机系统的心脏。CPU的构建与性能优化直接影响着计算机的运算速度和效能。随着科技的不断进步,CPU设计也在不断演进,从简单的单核结构到复杂的多核和并行计算,再到涉及超标量

    2024年02月01日
    浏览(61)
  • 『Linux从入门到精通』第 ⑫ 期 -深入了解冯诺依曼体系结构与操作系统(Operator System)

    🌸作者简介: 花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 Linux从入门到精通 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

    2023年04月23日
    浏览(83)
  • 『Linux从入门到精通』第 ⑫ 期 - 深入了解冯诺依曼体系结构与操作系统(Operator System)

    🌸作者简介: 花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。 🌸 专栏简介:本文收录于 Linux从入门到精通 ,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

    2024年02月10日
    浏览(48)
  • 深度学习体系结构——CNN, RNN, GAN, Transformers, Encoder-Decoder Architectures算法原理与应用

    卷积神经网络(CNN)是一种特别适用于处理具有网格结构的数据,如图像和视频的人工神经网络。可以将其视作一个由多层过滤器构成的系统,这些过滤器能够处理图像并从中提取出有助于进行预测的有意义特征。 设想你手头有一张手写数字的照片,你希望计算机能够识别出

    2024年04月28日
    浏览(50)
  • 软件设计模式与体系结构-软件体系-层次软件体系结构

    层次之间存在接口, 通过接口形成call/return的关系 ,上层是下层的客户端 层次系统的基本构件: 各层次内部包含的构件 连接件: 层间的交互协议 拓扑结构: 分层 拓扑约束: 对相邻层间交互的约束 层次软件体系结构(Layered Software Architecture)是一种常见的软件设计模式,

    2024年02月13日
    浏览(53)
  • OSI体系结构和TCP/IP体系结构

     在第一章( 计网第一章 )的时候,曾经提到过OSI体系结构和TCP/IP体系结构,并对它们进行了简单的对比。这篇博客在其基础上进行更深层次的理解。 计算机网络在逻辑功能上可以分为通信子网和资源子网两部分。 事实上,OSI将低三层称为通信子网,即为了联网而附加的通

    2024年02月07日
    浏览(73)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包