java内存分配问题初学

package com.jvmfinalstatic;


//需求分析:本节学习java虚拟机的内存方面问题
//参考文献:http://blog.csdn.net/yangyuankp/article/details/7651251

/**
 * @author ManagementAndJava
 */

/*
 *  1.了解类在运行是的信息
 *      1.1了解类的加载过程
 *      1.2了解 JVM中 方法区(和其中含有的常量池) 栈区    堆区   
 *  

 *  
 * 1.了解JVM运行机制和类的运行时信息
 *  1.1java内存分配分析:
 *      JVM是java程序与操作系统之间的桥梁,
 *      一个完成的java程序运行时会涉及到以下内存区域:寄存器; 栈; 堆;方法区; 常量池;
 *  1:栈:
 *      保存局部变量的值:存放 基本数据类型的值 (非对应的包装器类型)
 *          int x=3;在栈内存中开辟一块地址为OXFF101的内存,里面存的值是3;
 *      保存执行堆区对象或者常量池内对象的引用符号(指针),
 *          Integer x= 34;此时在方法区的常量池中开辟一个地址比如为(xof100)的内存,把数值34存放其中,在栈中开辟
 *          一个地址为OF200的内存把引用符号x(指向堆中地址xof100)存入;
 *          Student s = new Student();和上面一样;
 *      保存加载方法时的帧;
 *      特点:
 *          栈中的数据是共享的;
 *          无论是普通类型的变量还是引用类型的变量(俗称实例),都可以作为局部变量,他们都可以出现在栈中。只不过普
 *          通类型的变量在栈中直接保存它所对应的值,而引用类型的变量保存的是一个指向堆区的指针,通过这个指针,就
 *          可以找到这个实例在堆区对应的对象。因此,普通类型变量只在栈区占用一块内存,而引用类型变量要在栈区和堆区
 *          各占一块内存。
 *      每个线程都有自己的栈区,数据都是私有的其他栈不能访问
 *  2.堆:
 *      用来存放动态产生的数据,比如new产生的对象,数组。
 *      注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。
 *      因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方
 *      法复制一次;JVM只有一个堆区 被所有线程共享;
 *  3.方法区:
 *      用来存放已被加载的类的信息、常量、静态变量、即时编译器编译后的代码
 *      (在java中static的作用就是说明该变量,方法,代码块是属于类的还是属于实例的)。
 *      又称为静态区,其和堆一样被所有线程共享;其存储的都是在整个线程中唯一的元素如 class 和static
 *  4.常量池:
 *      JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。
 *      包括直接常量(基本类型包装器,String)和对其他类型、方法、字段的符号引用(1)。
 *      池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号
 *      引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于方法区(Method Area)内
 *      ( 1)、存放字符串常量和基本类型变量,比如String str=”www”; 实际上”www”是在常量池里边。
 *      (2)、常量池是在方法区中而不是堆内存中。
 *          (3)、 Java的8种基本类型包装器(Byte, Short, Integer, Long, Character, Boolean, Float, Double), 
 *              除Float和Double以外, 其它六种都实现了常量池, 但是它们只在大于等于-128并且小于等于127时
 *              才使用常量池。而如果大于127 或小于-128 则不会使用常量池所以会直接在堆内存中创建对象。。
 *  
 *  
 *       因为在java的八种基本类型: 基本类型(包装器类型) (普通变量(类))
 *       boolean (Boolean) char (Character) byte(Byte)  short(Short)    int(Integer)   long(Long)   
 *      float(Float)    double(Double)  
 *  
 *  简要总结:
 *  对于局部变量(包括基本数据类型变量和引用变量(区分基本类型包装器变量)) 基本数据类型变量会在栈中直接存放其数值,
 *  并且共享数值。引用变量,会在堆中创建对象,再栈中存放指向堆中地址的指针;而对于在-128——127之间的基本数据类型
 *  (6种再加上String)包装器对象,其数值存放在常量池中,引用存放在栈中;
 *  
 * */

//java常量池运用如下:
class ConstantPool{
    /*
     * 在jvm规范中,每个类型 都有自己的常量池。常量池是某类型所用常量的一个有序集合,包括直接常量(基本类型,String)和对其他类型、字段、
     * 方法的符号引用。 之所以是符号引用而不是像c语言那样,编译时直接指定其他类型,是因为java是动态绑定的,只有在运行时根据某些规则才能
     * 确定具体依赖的类型实例,这正是java实现多态的基础。
     */

     //常量池中的对象和堆中的对象

    public  static void method(){
        Integer i1 = new Integer(1);
        Integer i2 = new Integer(1);
        //i1和i2所对应的的对象分别位于 堆中 不同的内存空间
        System.out.println(i1==i2);//输出结果为  false 因为 new在堆中 引用i1和i2是在栈中存放各自堆中不同的地址

        int i3 =129;
        int i4 = 129;
        //变量的符号和数据都存放在栈中 并且只会在栈中存一份数据 i5=xxff102 i6=xxff102 同时指向129
        System.out.println(i3==i4);//输出结果为true;因为是在栈中只创建了一个数据两个变量符号同一个地址;

        /*
         * java中基本类型的 包装类 的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,
         * 另外两种 浮点数类型(Float Double) 的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型
         * 的包装类也只是在对应值-128-127时才可以使用常量池。
         * */

        //5种整形的包装类Byte,Short,Integer,Long,Character的对象,
           //在值小于127时可以使用常量池
           Integer i5=127;
           Integer i6=127;
           //i5和i6对对应的常量127存在常量池中,并且由于常量池中数据共享,所以在常量池中只会存在一个 127;而i5和i6这两个
            //变量符号是在栈中保存的(i5=xxffo12,i6=xxff012),并且指向常量池中同一个地址;
           System.out.println(i5==i6);//输出true

           //值大于127时,不会从常量池中取对象
           Integer i7=128;
           Integer i8=128;
           //在堆内存中分别建立两个 128 变量符号i7和i8分别保存在栈内存中,并且指向不同的地址;i7=xxf101 i8 =xxff451;
           System.out.println("i7==i8"+(i7==i8));//输出false

           //Boolean类也实现了常量池技术
           Boolean b1=true;
           Boolean b2=true;
           System.out.println("b1==b2"+(b1==b2));//输出true

           //浮点类型的包装类没有实现常量池技术 因此其数值保存在堆中 栈中存放对应堆中的地址;
           Double d1=1.0;
           Double d2=1.0;
           System.out.println("d1==d2"+(d1==d2));//输出false

           //String也实现了常量池技术,String类也是java中用得多的类,同样为了创建String对象的方便,也实现了常量池的技术
           String s1 = "helloJava";
           String s2 = "helloJava";
           System.out.println("s1 == s2 "+(s1==s2));//输出结果:true

           String s3 = new String("hellojava");
           String s4 = new String("hellojava");
           //属于new对象的  对象建立在堆中 引用变量存贮在栈中 ,并且栈中引用变量存的是两个对象的地址;
           System.out.println("s3 == s4"+(s3=s4));//输出结果必然 为false;

           String s5 = "hello";
           String s6 = "Java";
           //String s7= s5+s6;
           System.out.println("s5+s6==s1"+((s5+s6)==s1));//输出结果为false;
            //在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象

           final String s8 = "hello";
           String s9 = s8+"java";
           System.out.println("s8+java=s1 "+((s9==s1)));//输出结果为 false

           final String s81 = "hello";
           final String s91 =  "java";
           String s10 = s81+ s91;
           System.out.println("s81+s91=s1 "+((s10==s1)));//输出结果为 false

           final String s82 = "hello";
           String s92 =  "hello4";
           String s11 = s82+ 4;
           System.out.println("s82+4=s92 "+((s11==s92)));//输出结果为 true

           String s83 = "hello";
           String s93 =  "hello4";
           String s12 = s83+ 4;
           System.out.println("s83+4=s93 "+((s12==s93)));//输出结果为 false

           //思考为什么从 s5到s13的实例中会出现不同的结果:


           Integer in1=30;
           Integer in2 =1;
           Integer in3 =31;
           System.out.println(in3==in2+in1);//输出结果为true;Java的数学计算是在内存栈里操作的其实比较的是
                                            //基本类型(31=30+1),他们的值相同,因此结果为True。

    }

}
class Person1{//编译时其类型信息都会放入方法区

    private String name;//运行时 new Person1实例后 name 应有放在自己私有的栈中 ,name 对象放在堆区里
    public Person1(String name){
        this.name =name;
    }
    public void eat(){//eat方法本身放入方法区中;
        System.out.println("i can eat");
    }
}

public class  MemoryAllocation {
    /*
     * 系统收到开始命令后,启动JVM,这个进程首先从classpath路径中找到ReflectTest.class文件,读取这个文件中的二进制
     * 数据,然后将运行时的ReflectTest类的类信息存放到方法区中;(上述过程也被成为类的加载过程),紧接着JVM会从方法区
     * 中找打main()方法开始执行;
     * */

    public static void main(String[] args) {

        Person1 p1 = new Person1("Mike");
        /*当JVM读到这句话时,首先回去方法区中找Person1的信息,由于第一次加载发现没有,此时JVM会立即加载Person1L类,并且
         * 把Person1类的类型信息存放在方法区中,Java虚拟机首先就是在堆区中为一个新的Person1实例分配内存, 这个Person1实例
         * 持有着指向方法区的Person1类的类型信息的引用。实际上指的是Person1类的类型信息在方法区中的内存地址, 而这个地址,
         * 就存放了在Person1实例的数据区里。
         * 
         * 在虚拟机进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被
         * 称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过
         * 中的临时数据。
         * 
         * p1是一个在main()方法中定义的变量,可见,它是一个局部变量,因此,它被会添加到了执行main()方法的主线程的JAVA方 
         * 法调用栈中。而“=”将把这个p1变量指向堆区中的Person1实例,也就是说,它持有了引用。
         * */
        //p1.eat();
        /*
         * 接下来JVM执行P1.eat()方法时,JAVA虚拟机根据局部变量p1持有的引用,定位到堆区中的Person1实例,再根据Person1实例持有的引用,
         * 定位到方法去中Person1类的类型信息,从而获得eat()方法的字节码,接着执行eat()方法包含的指 令。
         * */

        ConstantPool.method();
    }

}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值