在jvm上分析类中变量,方法,实例分配规则

1 篇文章 0 订阅

基于之前一直关于成员变量,静态变量,局部变量,常量,方法,静态方法等等什么时候会变化,为什么会变化,值传递和对象传递为什么会有区别,总是记住但又不够理解深刻,于是总结一篇希望能帮助很多和我一样的同学尽快理解吧:

1.首先我们一个基本的类主要有以下东西:成员变量,静态变量,局部变量,常量,方法,静态方法等等

 

2.然后我们得明白jvm的分区大致分为:1)堆,2)栈,3)方法区,4)程序计数器,5)本地方法区

 

我们简单说下虚拟机的分区功能,

从程序计数器:主要针对多线程而言,一般一个线程有一个计数器,从而根据计数器的计数来对指令进行操作。

本地方法区:作用就是class中的native方法,本地方法的存储空间

接下来介绍我们类中一般使用的东西了:

方法区,它里面有一个常量池,所以它存储常量类似于1,2,3等可以理解为记录一个全局不变的变量所以static的变量也在里面,而且它还存放类的class对象,这就包括了这个类的field,method等(重点是method因为所有的method都被记录在这里而不是放在堆中)。

栈:根据栈的特性我们知道入栈出栈变动大,所以一般用来存放变量的,它里面有一个局部变量表用来存放局部变量的还包括方法出口的相关信息。

堆:存放的对象实例以及所有成员变量,切记所有生成的对象实例都是独立分开的。

提示:为何成员变量在堆中和局部变量在栈中?--------我的理解:堆中实例不存放方法信息,如果每个实例存放方法信息,堆内存很快就满了。所以堆不存在方法信息,而且方法信息都在class中所以我认为方法信息只有一份存在方法区中。至于为何局部变量,形参都放在栈中,是这样的。当我们调用方法时候,读取方法信息。包括局部变量而且全部入栈,当方法结束,全部出栈。无需gc操作。

实例:一个类中对应的内存分区和变化


package com.example.demo.domain;

/*
 *
 *     @author Qmh
 *     @Date 2019/3/28 15:43
 *
 */
public class Counter {
    int i;
    Counter c;

    public void add(int j) {
        this.i = this.i + j;
    }

    public void addPlus(int j) {
        j=j+1;
    }

    public void addCounter(Counter j) {
        j.i=j.i+1;
    }

    public static void main(String args[]) {
        Counter cas = new Counter();
        cas.i = 2;
        System.out.println(cas.i); //2
        
        Counter cas2 = new Counter(); //3
        cas2.i = 3;
        System.out.println(cas2.i); //3

        cas.add(1);
        System.out.println(cas.i); //3

        cas.addPlus(cas.i);
        System.out.println(cas.i); //3

        cas.addCounter(cas);
        System.out.println(cas.i); //4

    }
}

以这个类为例,首先jvm加载这个类之后

先把这个类的class对象的相关信息放到方法区中,这里面主要记录下来了她的方法。

接下来我们运行了main方法,然后执行  Counter cas = new Counter();

先把cas这个变量放到栈中因为它属于main这个方法中,每一个线程都有一个独立的栈内存用于存放变量的。而new Counter()则是生成一个对象实例,它是在堆中开辟一个空间用于存放了Counter的实例,具体开多大存哪些成员变量呢?就是从方法区中获得信息,而且栈中的变量cas指向了堆中的实例,随着方法结束,这个变量cas也要出栈,而其修改和指向的堆则还存在等待gc来处理。

接着cas.i=2 指出了在堆中这个cas对象实例里面的成员变量i=2,而2则是常量在方法区,i=2就是复制一份2。下面同理只是,cas2,在堆中是另一块区域了和cas是不可见的。

所以两个输出分别是2和3。

接下来分析下cas.add(1) 首先当前线程(先在方法区中class中获取method相关信息)然后在栈中加入变量 j,并把 j 赋值1此时,使用堆中cas里面的成员变量 i 并进行+1赋值操作,此时cas中的 i 就变成3了。因为操作在堆中

之后的cas.addPlus(cas.i) 进行,一样的先在方法区中class中获取method相关信息)然后在栈中加入变量 j 此时注意,j 不是直接指向 cas.i 的因为 cas.i 为常量所以读取它就是在常量区复制一份,并赋值个栈中的 j,所以此时j=3,j在栈中,j进行+1操作j=4,而此时方法结束,j出栈,对于cas.i在堆中没有任何变化。


 最后的cas.addCounter(cas)操作,首先当前线程(先在方法区中class中获取method相关信息)然后在栈中加入变量 j, 此时 j 不是拷贝一份方法区中常量池里的常量而是指向堆中的cas那块分区的实例对象,所以操作都是在堆中进行,自然是对cas中的i进行+1操作,值得一题的是c.i+1这里的c.i其实还是拷贝的常量池中的数据。这也就是为什么基本类型做形参和引用类型做形参的区别原因在这里。

大概的类基于jvm下的内存操作就是如此,我认为自己讲的已经够简单了吧。都是自己的一些理解如果有错误,请多指教。
      

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值