【Java基础】JVM详解

JVM是Java Virtual Machine(Java虚拟机)的缩写

运行过程
JAVA源代码通过编译器变成二进制字节码,字节码再通过解释器转行成机器码。
源代码 ----- 编译器 -----> 字节码
字节码 ----- 解释器 -----> 机器码

好处:

  • 一次编写,到处运行
  • 自动内存管理

一、JVM内存结构

JVM主要是由堆、栈、方法区、本地方法栈、程序计数器组成
方法区储存类信息,常量。静态变量等数据,是线程共享区域
栈又分为Java栈和本地方法栈,用于方法的执行,属于线程私有区域

1、本地方法栈

本地方法栈是保存native方法进入区域的地址
native方法是指Java代码调用非Java代码的接口,该代码的实现由非Java语言实现。该方法不提供实现体,例如:

private native void test();

2、程序计程器

程序计数器是计算机处理器中的寄存器,它包含当前正在执行的指令的地址(位置)。当每个指令被获取,程序计数器的存储地址加一。在每个指令被获取之后,程序计数器指向顺序中的下一个指令。当计算机重启或复位时,程序计数器通常恢复到零。

作用

记录下一条JVM指令的执行地址

特点
  • 线程私有
  • 不会出现内存溢出

3、方法区

方法区储存类信息,常量。静态变量等数据,是线程共享区域

4、栈

栈又名堆栈,每个线程运行时所需要的内存,被称为栈,由多个栈桢组成。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
上溢和下溢都被称为栈溢出

5、堆

堆由新生区、老年区,永久区组成。垃圾回收主要在新生代(伊甸园区)和老年代。如下图所示:

  • 新生代:由伊甸园(Eden)、幸存区0(ServivorFrom)、幸存区1(ServivorTo)组成,当三个区域都满了,会触发一次重量级GC
    • 伊甸园:是java新生的对象存放的地方,当该区域内存满了会触发一次轻量级GC,对新生代进行一次垃圾回收
    • ServivorTo:保留一次GC存活的对象
    • ServivorFrom:上一次GC的存活者,转移到该区域进行扫描
  • 老年代:当一些对象经历了多次重量级GC(一般是15次),就会被转移到老年代
  • 永久区:内存的永久保存区域,主要存放Class和Meta(元数据)的信息。在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。储存的是java运行时的环境或者类信息,该区域不存在垃圾回收。

内存溢出时诊断的工具
jvisualvm、jconsole

二、垃圾回收(GC)

计算哪些是垃圾的算法:引用计数法、可达性分析
GC的过程的算法:复制算法、标记清除算法、标记清除整理算法

1、引用计数法

  • 含义:该算法是给每个对象记录被引用的次数,如果该对象被引用,计数器就+1,如果取消引用,该计数器就-1,如果该对象的计数器为0,那么说明该对象可以被回收。
  • 举例:如下述代码所示:test()方法中创建了一个对象,当main方法调用test方法时,该对象的计数器便会+1,当方法结束时,该对象的计数器便会-1
	public static void main(String[] args) {
        test();
    }
    
	public static void test(){
        Test2 test2 = new Test2();
    }
  • 问题
    1、无法处理循环引用的问题
    2、每创建一个对象便会进行一次加减计数,导致性能开销较大

2、复制算法

复制算法主要是针对幸存0区(ServivorFrom)和幸存1区(ServivorTo)
GC过程

  • 当我们进行垃圾回收时,发现form区中有8个对象,其中有3个已经死亡。
  • 垃圾回收时,将存活对象移到to区
  • 清除from区中的死亡对象
  • 这时,form区就变成了to区(from区和to区并不是固定的,而是随着每一次的垃圾回收不断的变化,识别的方法主要是谁空谁就是to区)

    优劣势
  • 优势:比较高效
  • 劣势:to区永远为空,损失了一半的可用内存。并且form内存空间碎片化,可用空间并不是一整块

3、标记清除算法

过程

  • 标记:扫描内存,将存活对象标记出来
  • 清除:将未标记的垃圾对象清除

优劣势:
优势:不需要额外空间
劣势:两次扫描,浪费时间,产生内存碎片

4、标记清除压缩算法

多了一次扫描,解决了标记清除算法中内存碎片的问题
过程

  • 标记:扫描内存,将存活对象标记出来
  • 清除:第二次扫描,将未标记的垃圾对象清除
  • 整理:第三次扫描,整理内存

三、总结

  • 年轻代存活率低,适合用复制算法
  • 老年代存活率高,适合标记清除和标记压缩
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,hashCode()是Object类的一个方法,返回对象的哈希码。哈希码是由对象的存储地址或者其他信息计算得到的一个int类型的整数。 在Java中,哈希码经常被用来实现数据结构中的散列表。在散列表中,每个对象都有一个对应的哈希码,通过哈希码可以快速定位到对象存储的位置,从而实现快速的查找、插入和删除等操作。 Java中的hashCode()方法的默认实现是返回对象的存储地址的一个int类型的整数表示,即对象的内存地址。但是,不同的JVM实现可能会有不同的实现方式。 在实际开发中,我们可以根据对象的属性计算哈希码,以实现更好的散列表性能。具体来说,我们需要注意以下几点: 1. hashCode()方法的返回值应该尽可能地分散,避免不同的对象产生相同的哈希码。这可以通过使用对象的不同属性进行计算来实现。 2. 如果两个对象的equals()方法返回true,那么它们的hashCode()方法应该返回相同的值。这可以确保这两个对象在散列表中的位置是相同的。 3. hashCode()方法的返回值应该在对象的生命周期中保持不变。如果对象的属性发生变化,那么它的哈希码也应该发生变化。 总之,hashCode()方法在Java中是非常重要的一个方法,我们可以根据对象的属性计算哈希码,以实现更好的散列表性能。同时,我们需要注意hashCode()方法的实现要尽可能地分散,避免不同的对象产生相同的哈希码,同时也要保证hashCode()方法的返回值在对象的生命周期中保持不变。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值