java内存图理解 & this关键字

 

Part1:this关键字


1. 概念

this 代表所在类的对象引用(内存地址)

理解:

this一般出现在类内部使用,标识的是当前类(当前对象);

Java虚拟机(JVM)会给每个对象分配一个this,来代表当前对象

【案例代码】

public static void main(String[] args) {
        new User(30).getAge();
    }


package org.demo01;

public class User {
    private Integer age;

    public User(Integer age) {
        this.age = age;
    }

    public void getAge() {
        int age = 18;
        System.out.println("这里是局部变量:" + age);
        System.out.println("这里是类的属性:" + this.age);
    }

}

 【输出结果】

这里是局部变量:18
这里是类的属性:30

2. 案列结论

【案例代码】

package org.example;

public class User {
    private String name;

    public void setName(String name) {
        System.out.println("进入setName方法"+this);
        this.name = name;
    }
}
package org.example;

public class Main {
    public static void main(String[] args) {
        User user = new User();
        System.out.println("打印对象名:   "+user);
        user.setName("张三");
        System.out.println("---------------------");
        User user2 = new User();
        System.out.println("打印对象名2:   "+user2);
        user.setName("李四");
    }
}

 【输出结果】

根据打印的内存地址一样,可以得出结论:类中定义的this,就是当前调用的对象。 

打印对象名:   org.example.User@4554617c
进入setName方法org.example.User@4554617c
---------------------
打印对象名2:   org.example.User@74a14482
进入setName方法org.example.User@4554617c

  其他参考:【Java】还不懂this关键字?一分钟彻底弄懂this关键字_java this-CSDN博客

Part2:对象内存图


1. 内存图 

最终上面代码段输出:

  • 堆内存地址:001
  • 堆内存变量默认值:null...0
  • 赋值后的变量:阿强...23
  • 占内存执行方法:好好学习

2. 方法区(method area)


作用:存储字节码文件信息(.class)

还包含:静态变量、常量;成员变量、成员方法。

方法区用于存储类的结构信息(如类的字段、方法、构造函数等)、静态变量、常量、方法字节码等。

在Java 8及之前的版本中,方法区是永久代(Permanent Generation)的一部分;

而在Java 8及之后的版本中,永久代被移除,取而代之的是 元空间(Metaspace)


3. 栈内存(stack)


栈主要用于存储局部变量和方法的调用信息(包括参数、返回地址等)。

每个线程都有自己的栈,用于存储该线程执行方法时的局部变量和部分结果。

当方法被调用时,会在栈上创建一个新的栈帧(Stack Frame),用于存储该方法的局部变量等信息。
 

方法的运行在栈内容;存放的都是方法中的局部变量。

方法的运行一定要在栈当中。

局部变量: 方法中的参数,或者方法{}内部的变量。

作用域: 一旦超出作用域,立刻从栈内存当中消失。

4. 堆(heap)

【定义】
堆(Heap)是Java虚拟机(JVM)管理的内存中最大的一块区域,它被所有线程共享,用于存放对象实例以及数组。几乎所有的对象实例(通过new关键字创建的对象)都在这里分配内存。

【特点】

线程共享:堆内存是线程共享的,这意味着多个线程可以同时访问堆中的对象,但需要注意线程安全问题。

大小可调:堆内存的大小可以通过JVM启动参数(如-Xms-Xmx)进行调整,以控制JVM的初始堆大小和最大堆大小。

地址表示:堆内存中的每个对象都有一个唯一的地址值,这个地址值通常是16进制的。

默认值:堆内存中的对象数据(包括对象的属性)在初始化时会被赋予默认值(如整数类型的默认值为0,对象引用的默认值为null等)。

规则:

如果是整数 默认为0

如果是浮点数 默认为0.0

如果是字符 默认为’\u0000’

如果是布尔 默认为false

如果是引用类型 默认为null

【注意】

成员方法本身并不存储在堆内存中,而是存储在方法区(或元空间)中。堆内存中仅保存指向方法区(或元空间)中方法的引用地址。

堆内存的管理和垃圾回收是JVM自动进行的,但开发者可以通过编写高效的代码和合理的JVM参数设置来优化堆内存的使用和垃圾回收的性能。

4. 程序计数器(Program Counter Register)

程序计数器是每个线程私有的内存区域,用于存储当前线程正在执行的字节码指令地址。在多线程环境下,程序计数器可以看作是当前线程执行的位置指示器。

5. 扩展问题

【问题】方法区的class信息什么时候过期/消失?

【分析】当类被加载到JVM中,其相关信息(包括类的方法、变量等)就会被放入方法区。当类不再被使用,且垃圾收集器判断该类没有实例存在,同时没有任何引用指向该类的类加载器(ClassLoader)及其类对象时,该类可能会被卸载,此时方法区中关于该类的信息也将不再有效,该类信息被JVM从内存中移除。

Part3:两个引用指向同一个对象

【解读】

大家都知道stu1指向内存001,stu2=stu1其实也就是把001地址赋给stu2.

当把 stu1 = null 后,那么stu2 是不是也是null呢?

答案是否定的,stu1=null,可以看做stu1原本指向的内存地址,赋值为null。

而stu2 依然记录的是堆内存地址001,所以stu2依然有数据。

【垃圾回收】
当堆内存中,对象或数组产生的地址,通过任何方式都不能被找到后,就会被判定为内存中的“垃圾垃圾会被Java垃圾回收器,空闲的时候自动进行清理

Part4:堆内存

堆内存被逻辑上划分为几个区域,以优化垃圾回收(GC)的性能:

  • 年轻代(新生代):用于存放新生成的对象。年轻代又可以进一步细分为Eden区、两个Survivor区(From和To或S0和S1)。当Eden区满时,会触发Minor GC(Young GC),将存活的对象复制到Survivor区,并清空Eden区。
  • 老年代(养老区):用于存放那些在年轻代中经历了多次垃圾回收后仍然存活的对象,以及一些大对象(直接分配在老年代中)。老年代的垃圾回收称为Major GC(Full GC),通常发生在老年代内存满时。
  • 永久代/元空间(Method Area的替代):在JDK 1.8之前,方法区被称为永久代,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。但在JDK 1.8及以后,永久代被元空间(Metaspace)所取代,主要目的是解决永久代内存溢出的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫为善

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值