双非本科准备秋招(8.2)——JVM1

第一天系统学习JVM!今天学了JVM是什么,学习JVM的作用,运行时的数据区域(重点),内存溢出。明天学GC。

运行时数据区域

整体认识

JDK1.7
JDK1.8

先写一下每个线程私有的三个数据区,分别是程序计数器,虚拟机栈,本地方法栈。

然后再写一下堆和方法区(概念,1.7的实现是永久代,1.8的实现是元空间)

程序计数器

作用:

1、记住下一条jvm指令的执行地址,一个线程的运行就是在它的程序计数器的变化下推动的。

2、字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

3、多线程环境,线程来回切换时,线程自身的程序计数器能记住线程执行指令的位置。

特点:

1、是一块很小的内存空间,运行速度最快的存储区域。

2、线程私有,每个线程都有自己的程序计数器。

3、唯一的JVM规范中没有规定OutOfMemoryError的区域,因为它存储的是地址,占用内存小,几乎可以忽略不计。

虚拟机栈

概念

每个线程的创建的时候都会创建一个虚拟机栈,线程私有的,其实就是线程运行时需要的内存。

每个栈内由栈帧(Stack Frame)组成,实际上就是一个个java的方法,每个线程只能有一个活动栈帧(当前栈帧),就是只能执行当前一个方法。

内存溢出(爆栈)

1、如果线程请求的栈深度大于JVM允许的深度,抛出StackOverFlowError。比如无限递归。

2、如果栈扩展时无法申请足够的内存,抛出OutOfMemoryError异常

栈帧及其结构

方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。return指令和抛出异常会使当前栈帧被弹出,继续执行下一个栈帧。

本地方法栈

本地方法在java中被native关键字修饰,可以看到本地方法没有方法体,它并不是java语言编写的,而本地方法栈就是为虚拟机使用Native方法服务的。HotSpot虚拟机(Oracle维护的java虚拟机,JVM只是一种规范,遵循JVM规范实现的java虚拟机有很多)中,本地方法栈和虚拟机栈合二为一。

我们用的Object类的clone、hashCode、notify、wait都是本地方法。

概念

内存最大的区域,用来存放对象实例,几乎所有对象实例和数组在此分配内存。

为什么说几乎?见以下引用:

Java 世界中“几乎”所有的对象都在堆中分配,但是,随着 JIT 编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。从 JDK 1.7 开始已经默认开启逃逸分析,如果某些方法中的对象引用没有被返回或者未被外面使用(也就是未逃逸出去),那么对象可以直接在栈上分配内存。


著作权归JavaGuide(javaguide.cn)所有 基于MIT协议 原文链接:https://javaguide.cn/java/jvm/memory-area.html

java堆是GC管理的区域,垃圾会分为各种代,分代的目的是为了优化GC性能。

内存溢出

可以用-Xmx和-Xms控制堆的大小。

如果堆满了,会抛出OutOfMemoryError

方法区

概念

JVM并未规定方法区的实现,所以不同虚拟机中的实现都不相同,HotSopt虚拟机,简单来说,JDK1.7的实现是永久代,存在堆内存,JDK1.8彻底放弃了永久代,改为元空间,存在操作系统的内存中。

虚拟机要使用类的时候,会解析*.class文件获取信息,然后将信息存入方法区,方法区保存类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

常量池

java堆中的字符串常量池,用来保存字符串对象。

看一下test类反编译后的class文件,可以看到编译器给它加了个无参构造,并且合并了"a"+"b"

在终端执行java -v *.class命令,可以反编译,查看详细信息。

这个Constant Pool就是我们的常量池,#1等是每个字面量的地址

往下看可以看到JVM指令:

ldc命令判断常量池是否保存了对应字符串对象的引用,保存了就返回,没保存就在堆中创建字符串对象,并且将该字符串对象的引用保存到常量池中。

常量池中的信息会被加载到运行时常量池,但是一开始这些字符只是常量池的符号而已,还不是真正的java字符串对象,等到执行ldc #2这条命令的时候,才会创建字符串对象"a"。

运行时常量池

方法区的一部分,存放常量池表。

如下问题,继续加深对String的理解。

        String s1 = "a";
        String s2 = "b";
        String s3 = "a" + "b";
        String s4 = s1+s2;
        String s5 = "ab";
        String s6 = s4.intern();

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);

        String x2 = new String("c") + new String("d");
        x2.intern();
        String x1 = "cd";
        
        System.out.println(x1 == x2);

答案是false;true;true;true;

s3==s4?

s1+s2做了什么?我们看看反编译后的jvm指令。

这里就能清晰地看出,new了一个StringBuilder对象,调用append方法,最后调用toString返回了String对象,并没有使用ldc指令,所以这个String对象创建在堆内存,不在常量池中。所以s3和s4指向不同的地址。

s3==s5?

很明显相等,都执行ldc指令,二者都是常量池中"ab"的地址。

s3==s6?

intern()方法做了什么?

这是个native方法,jdk1.8中,将这个字符串对象尝试放入常量池,如果有不放入;如果没有则放入,并且返回常量池中的对象。

所以s3和s6都指向常量池的对象,相等。

s1==x2?

    String x2 = new String("c") + new String("d");
    x2.intern();
    String x1 = "cd";

常量池一开始没有"cd",x2.intern在常量池中创建了"cd"并返回它的地址,所以x1==x2.

那如果颠倒一下呢?

    String x2 = new String("c") + new String("d");
    String x1 = "cd";
    x2.intern();

执行x2.intern,发现常量池中已经有了"cd",于是啥都不做,所以x1!=x2

但是注意JDK1.7的intern有所不同,再执行s.intern()时,会先拷贝一份s对象,然后放入常量池,返回常量池对象,如果没有也是啥也不干。

所以要是JDK1.7的话,那么下面就是false了,因为x2执行intern,intern返回的值不是给x2,而是拷贝出来的x2,原x2还是堆内存中的String。

    String x2 = new String("c") + new String("d");
    x2.intern();
    String x1 = "cd";

今天学习JVM让我对String的理解真的通透了!

本地内存和直接内存

可以这么表示:JAVA程序内存 = JVM内存+本地内存(元空间+直接内存)

本地内存(NativeMemory):并不属于虚拟机运行时数据区,而是本机的物理内存,只有申请内存超过本机物理内存才会抛出OutOfMemoryError异常。

直接内存(Direct Memory):JDK1.4加入了NIO(new input/output),可以通过存储在java堆里的DirectByteBuffer对象作为这块内存的引用操作,提高性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/355003.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker—入门及Centos7安装

1、Docker入门 1.1、Docker是什么? Docker是基于Go语言实现的云开源项目。 Docker的主要目标是“Build,Ship,and Run Any App,Anywhere”,也就是通过对应组件的封装、分发、部署、运行等生命周期的管理,使用户的APP&…

模板笔记 ST表 区间选数k

本题链接:用户登录 题目: 样例: 输入 5 3 1 1 2 2 3 1 2 3 3 1 5 输出 4 6 思路: . 根据题意,给出数组,以及多个区间,问这些区间中,最小值之和 和 最大值之和,…

CHS_02.2.3.2_1+进程互斥的软件实现方法

CHS_02.2.3.2_1进程互斥的软件实现方法 知识总览如果没有注意进程互斥?单标志法双标志先检查法双标志后检查法Peterson 算法 知识回顾 在这个小节中 我们会学习进程互斥的几种软件实现方法 知识总览 那我们会学习单标志法 双标志 先检查 双标志后检查和Peterson算法…

前端工程化基础(三):Webpack基础

Webpack和打包过程 学习webpack主要是为了了解目前前端开发的整体流程,实际开发中,我们并不需要去手动配置,因为框架的脚手架都已经帮助我们完成了配置 内置模块path 该模块在Webpack中会经常使用 从路径中获取信息 const path require(&qu…

前端Vue v-for 的使用

目录 ​编辑 简介 使用方式 基本使用 v-for"(item, index)中item和index作用 示例 迭代对象 示例 结果 前言-与正文无关 生活远不止眼前的苦劳与奔波,它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中,我们往往容易陷入…

【Linux】第三十八站:信号处理

文章目录 一、信号处理二、再谈进程地址空间三、内核如何实现信号的捕捉四、sigaction 一、信号处理 我们知道,信号保存以后,会在合适的时候进行处理这个信号。 那么信号是如何被处理的?什么时候进行处理呢? 当我们的进程从内核…

精通Python第13篇—数据之光:Pyecharts旭日图的魔法舞台

文章目录 引言准备工作绘制基本旭日图调整颜色和样式添加交互功能定制标签和标签格式嵌套层级数据高级样式与自定义进阶主题:动态旭日图数据源扩展:外部JSON文件总结 引言 数据可视化在现代编程中扮演着重要的角色,而Pyecharts是Python中一个…

【深度学习每日小知识】Bias 偏差

计算机视觉是人工智能的一个分支,它使机器能够解释和分析视觉信息。然而,与任何人造技术一样,计算机视觉系统很容易受到训练数据产生的偏差的影响。计算机视觉中的偏见可能会导致不公平和歧视性的结果,从而使社会不平等长期存在。…

Python进阶(1) | 使用VScode写单元测试

Python进阶(1) | 单元测试 2024.01.28 VSCode: 1.85.1 Linux(ubuntu 22.04) 文章目录 Python进阶(1) | 单元测试1. 目的2. Python Profile3. 单元测试框架3.1 什么是单元测试3.2 选一个单元测试框架3.3 编写 Python 单元测试代码3.4 在 VSCode 里发现单元测试3.5 再写一个单元…

问题:github上不了,但是其他网页可以正常打开

问题: github上不了,但是其他网页可以正常打开,试了关闭防火墙,dns刷新,都没用后,参考以下文章成功打开Github 1.Github无法访问解决方法 2.github访问不了?详细解决方法 解决办法&#xff1a…

用Python编写的简单双人对战五子棋游戏

本文是使用python创建的一个基于tkinter库的GUI界面,用于实现五子棋游戏。编辑器使用的是spyder,该工具。既方便做数据分析,又可以做小工具开发, 首先,导入tkinter库:import tkinter as tk,这…

leetcode刷题日志-146LRU缓存

思路:使用hashmap储存key,vaule,使用双向链表以快速查到尾结点(待逐出的节点),链表的题一定要在纸上画一下,不然连着连着就不知道连在哪里去了 class LRUCache {public class ListNode {int ke…

Java基础常见面试题总结(下)

常见的Exception有哪些? 常见的RuntimeException: ClassCastException //类型转换异常IndexOutOfBoundsException //数组越界异常NullPointerException //空指针ArrayStoreException //数组存储异常NumberFormatException //数字格式化异常ArithmeticE…

【Mac】windows PC用户转用Mac 配置笔记

win转mac使用的一些配置笔记;感觉mac在UI上还是略胜一筹,再配合在win上的操作习惯就体验更好了,对日常办公需求的本人足以。 优化设置 主要 操作优化 AltTab: win 习惯查看全部活动的alt键,对比cmdtab多了可以预览&…

前端——JavaScript

目录 文章目录 前言 一. JavaScript基础 1.JavaScript基本结构 2. JavaScript 执行过程 3. JavaScript 引入方式 二. JavaScript 语法 1.数据类型 2.变量 2.1 var 关键字定义变量 2.2 let 关键字定义变量 2.3 var 与 let 的区别 3.字符串 3.1定义字符串 3.2 字…

Px4学习:进入控制台的方法

运行命令 ls /dev/tty* 会列出所有端口 然后连接飞控通过USB数据线连接到电脑,再运行一次,就可以找到 笔者的是ttyACM0,下面会用到 px4源码 1.13.3 进入控制台 进入PX4源码文件夹,用终端打开,运行命令 ./Tools/mav…

Qt|大小端数据转换

后面打算写Qt关于网络编程的博客,网络编程就绕不开字节流数据传输,字节流数据的传输一般是根据协议来定义对应的报文该如何组包,那这就必然牵扯到了大端字节序和小端字节序的问题了。不清楚的大小端的可以看一下相关资料:大小端模…

jenkins对接K8S

创建连接K8S的凭据 查看需要使用到的命名空间 [rootk8s ~]# kubectl get ns |grep arts-system arts-system Active 16d创建service accounts [rootk8s ~]# kubectl create sa jenkins-k8s -n arts-system serviceaccount/jenkins-k8s created [rootk8s ~]# kubectl…

log4j2 配置入门介绍

配置 将日志请求插入到应用程序代码中需要进行大量的计划和工作。 观察表明,大约4%的代码专门用于日志记录。因此,即使是中等规模的应用程序也会在其代码中嵌入数千条日志记录语句。 考虑到它们的数量,必须管理这些日志语句,而…

CTF CRYPTO 密码学-7

题目名称:敲击 题目描述: 让我们回到最开始的地方 0110011001101100011000010110011101111011011000110110010100110011011001010011010100110000001100100110001100101101001101000011100001100011001110010010110100110100011001000011010100110000…