`
zhanghq0717
  • 浏览: 4271 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

分享~~黑马程序员-java内存结构和对象创建的过程

jvm 
阅读更多
<html>
<head>
<title>测试</title>
</head>
<body>
<div style="background-image:url('http://img4.imgtn.bdimg.com/it/u=1891438357,4288680421&fm=23&gp=0.jpg')"></div>
</body>
</html>

String str1 = new String("abc"); 
String str2 = new String("abc"); 
String str3 = "abc"; 
String str4 = "abc"; 
String str5 = "ab"+"c"; 

System.out.println(str1 == str2); //1 
System.out.println(str1 == str3); //2 
System.out.println(str3 == str4); //3 
System.out.println(str4 == str5); //4 

程序依次输出的值是: 
false 
false 
true 
true 

转载文章,转载出处:http://www.oschina.net/question/1758033_155995



在学到毕老师视频中介绍关于java内存结构的时候,感觉这些内容特别重要。结合视频和对《深入理解java虚拟机》这本书的一些理解写了这篇日记。
java内存分配和管理是java的核心技术之一。一般java程序运行时会涉及到如下几个存储区域:

程序计数器:
可以看做是当前所执行字节码的行号指示器。通常是改变计数器字节码的值来抉择下一步索要执行的代码,程序员不直接控制。

寄存器:
在程序中我们无法直接控制,由编译器管理。

栈区:  
1,每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义的对象的引用以及局部变量。


2,每个栈中的数据都是私有的,其他栈不能访问。


3,当局部数据用完时,所占内存空间会自动释放。
JVM栈一个最大的用处是为执行 java方法服务的,每一个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。
每一个方法从调用直至执行完成的过程都对应着一个栈帧在虚拟机中从入栈到出栈的过程。
栈的优势在于存取速度比较快,仅次于寄存器。缺点是存储在栈中的数据的大小和生存周期是确定的,缺乏灵活性。


堆区:
1,通过new建立的数组和对象都存储在堆中。


2,每一个对象都有相应的内存地址值。


3,对象中的变量都有默认初始化值。


4,数组和对象在没有引用变量指向它的时候,才变成垃圾,不能再被使用,但是仍然占着内存,在随后的一个不确定的时间被垃圾回收器释放掉。


另外要明确的是每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。在java堆中还必须包含能查找到对象类型数据(如对象类型,父类,实现的接口,方法等)的地址信息,
这些类型数据(优先于对象存在)都存储在方法区中。
堆的优势在于可以动态地分配内存的大小,生存周期也不必事先告诉编译器,因为它是运行时动态地分配内存的,并用垃圾回收机制回收那些不用的数据。缺点是存取速度比较慢。


方法区:
方法区本身又分为以下几个区域:
1.静态数据区,存放静态数据的地方

2.常量区:就是下面要介绍的常量池。

3.代码区:存放方法代码的区域
总之,方法区是JVM在装载类文件时,用于存储类型信息的(类的描述信息)地方,是一片给各个线程共享的内存区域。

常量池:
常量池是Class文件结构中与其他项目关联最多的数据类型,它本身是方法区中的一个数据结构。在方法区中,每个类型都对应一个常量池,
常量池中存储了诸如文字字符串、final变量值、类名和方法名常量。JVM把常量池组织为入口列表的形式,可通过索引来访问常量池中的各个入口,
并用入口标识来表示该常量池存储的数据类型。常量池在编译期间就被确定,并被保存在已编译的.class文件中的一些数据,一般分为两大类,字面量和引用量。
字面量入全面提到的文本字符串,final变量等。类名和方法名属于引用量。
引用量最常见的一种用法是在调用方法的时候,根据方法名找到方法的引用,并以此定位到函数体进进行函数代码的执行。
引用量:
1,类和接口的权限定名。
2,字段的名称和描述符。
3,方法的名称和描述符。

通过String来了解常量池,例如有如下五个赋值语句:
String str1 = new String("abc"); 
String str2 = new String("abc"); 
String str3 = "abc"; 
String str4 = "abc"; 
String str5 = "ab"+"c"; 

System.out.println(str1 == str2); //1 
System.out.println(str1 == str3); //2 
System.out.println(str3 == str4); //3 
System.out.println(str4 == str5); //4 

程序依次输出的值是: 
false 
false 
true 
true 

首选要说名的是用String str = "abc";这种方式创建的字符串对象是存储在常量池中的,JVM首先去字符串常量池里找有没有"abc",没有,则将"abc"放入常量池,否则直接将指向常量池中"abc"的引用给str。而通过new创建的字符串对象是存储在堆内存中的。"=="运算符比较的是对象的引用值,str1和str2都有自己的对应的堆空间,故引用值不等,输出false。通过new创建的对象永远都不可能存储在常量池空间!!,故第二个也输出false。java确保常量池中的一个字符串常量只有一个拷贝。故第三个输出为true。当一个字符串有多个字符串常量连接而成时,它本身也是一个字符串常量,所以str5本身也是一个字符串常量的引用,它指向常量池中的"abc",从而第四个也输出true。


以创建Pernson对象为例,分析对象创建过程中在内存中的分配情况以及方法的调用过程。
class Person 
{ 
   private String name; 
   private int age; 
   private static String Country = "CN"; 


   DemoTest dTest = new DemoTest(); 


   public Person(String name, int age) 
  { 
       System.out.println("这是person的构造函数"); 
       this.name = name; 
       this.age = age; 
   } 


  { 
       System.out.println("这是person的构造代码块"); 
   } 


   static  
  { 
       System.out.println("这是person类的静态代码块"); 
   }    
   public void setName(String name) 
  {www.huiyi8.com 
       this.name = name; 
   } 


   public void show() 
  { 
      System.out.println("name = "+name + "::"+"age = "+age); 
   } 


   public static void showCountry() 
  { 
      System.out.println("Country = "+Country); 
   } 
} 


class DemoTest 
{ 
     public DemoTest() 
    { 
         System.out.println("这是一个测试的类"); 
     } 
} 


public class test 
{ 
     public static void main(String[] args) 
    { 
         Person p = new Person("zhangsan",11); 
         p.setName("lisi"); 
         p.show(); 
     } 
} 


main函数执行后的结果为:


这是person类的静态代码块
这是一个测试的类
这是person的构造代码块
这是person的构造函数
name = lisi::age = 11


过程分析:
首先要明确的一点是: 类的成员变量在不同对象中各不相同,都有自己的存储空间(存储在各自对象所占用的堆内存空间中)。
而类的方法却是该类的所有对象共享的,它们存放在了方法区中,各方法的引用存储在了常量池中。方法区优先于对象存在而且对象只保存了成员变量和一些地址信息。


首先栈中的main函数执行Person = new Person("zhangsan",11);这个简单的语句会涉及到如下几个步骤:
1,由于是要创建Person类对象,java虚拟机(JVM)先去找Person.class文件,如果有的话,将其加载到内存。


2,将类型信息(包括静态变量,方法等)加载进方法区。


3,执行该类中static代码块,如果有的话,对Person.class类进行初始化。(此时输出‘这是person类的静态代码块’)


4,到这时才进行堆内存空间的开辟,并为对象分配首地址。


5,在堆内存中建立对象的成员属性,并对其进行初始化(先进行默认初始化再进行显示初始化。(此时输出  ‘这是一个测试的类’)


6,进行构造代码块的初始化,由此看出构造代码库初始化的优先级要高于对象构造函数的初始化。 (此时输出 ‘这是person的构造代码块’)

7,对象的构造函数进行初始化。(此时输出 ‘这是person的构造函数’);

8,将堆内存中的地址付给栈内存中的p变量。

接下来执行p.setName("lisi")过程如下:
1,首先在栈中为对象p开辟一个栈空间供执行方法时使用,每个对象在执行自己的方法的时候得到自己的一份栈空间。然后根据引用p定位到堆中的对象,再根据对象持有的引用定位到方法区中的函数setName(),同时获得方法的字节码(引用)。

2,在栈中压入一个局部数据作为方法的实参,通过方法字节码,程序计数器跳转到相应的方法中执行,
并将调用此方法的对象的引用赋值给this,接着便将实参赋值给this所指向的对象在堆中相应的成员变量。

3,方法调用结束,会通过执行类似汇编中的ret指令,自动释放局部数据所占用的空间,并重置程序计数器的值以执行main函数中调用方法语句后的下一条语句。

注意: 每一个不同的对象(由相同的类创建而成)在执行同一个函数时,引用的是方法区中同一个字节码!!

执行p.show()的过程与执行p.setName("lisi")的过程类似。(输出 ‘name = lisi::age = 11’)。

以上是我的java虚拟机机制的一些理解,由于JVM的复杂性,完全理解它是很困难的,仅仅是写下这篇浅显的日志也花费了大量的时间去理解。
分享到:
评论

相关推荐

    黑马程序员 - Java基础教学 - 05 - 面向对象(1).doc

    在《黑马程序员 - Java基础教学 - 05 - 面向对象(1)》文档中,详细讲解了面向对象的基本概念及其在Java中的应用。 ### 面向对象与面向过程 面向对象和面向过程都是解决问题的思维模式,但面向对象更注重将功能和...

    黑马程序员 - Java基础教学 - 03 - 变量的作用域、for循环、break、continue、内存结构

    ### 黑马程序员Java基础教学知识点详解 #### 一、变量的作用域与生命周期 变量的作用域定义了变量的可见性和生存周期。在Java中,变量根据其声明位置的不同,具有不同的作用域。例如,在给定的示例代码中: ```...

    黑马程序员入学Java精华总结

    ### 黑马程序员入学Java精华总结 #### 一、Java概述与基础知识 1. **何为编程?** - 编程是指通过编写计算机能够理解的指令来解决问题或完成特定任务的过程。这些指令通常被组织成算法,并使用某种编程语言实现。...

    黑马程序员 - Java基础教学 - 06 - 面向对象(2) - 关于静态static的那些事.doc

    - 静态方法不能访问非静态成员,因为静态方法在对象创建之前就可以调用,而非静态成员依赖于对象存在。 - 静态方法内不能使用this和super关键字,因为它们都与对象有关。 5. 静态的利与弊: - 利:节省内存,...

    黑马程序员Javase笔记

    "黑马程序员Javase笔记"是一个自学者在学习黑马程序员提供的Java全套课程过程中整理的笔记,主要涵盖了Java Standard Edition (Javase) 的核心内容。下面将详细讨论其中的关键知识点。 首先,DOS命令是操作系统中的...

    黑马程序员入学Java知识(精华总结)

    ### 黑马程序员入学Java知识(精华总结) #### 一、Java概述与基础知识 ##### 1、何为编程? 编程是指使用计算机语言来编写指令,这些指令被计算机执行以完成特定任务的过程。通过编程,我们可以控制计算机的行为...

    黑马程序员入学Java知识——精华总结.doc

    - **面向对象**:Java强调类和对象,支持封装、继承和多态,便于构建复杂的软件系统。 - **安全性**:Java提供了安全管理机制,防止非法代码破坏系统。 - **跨平台性**:Java的“一次编写,到处运行”(Write ...

    2023黑马面试宝典-Java面试宝典大全-java面试宝典黑马

    Java面试宝典是Java程序员求职面试的重要参考资料,它涵盖了Java编程语言的核心概念、高级特性、设计模式、并发处理、框架应用、数据库交互等多个方面。以下将详细解析这些关键知识点: 1. **Java基础**:面试中,...

    黑马程序员java面试宝典 完整版PDF.rar

    《黑马程序员Java面试宝典》是一本专门为Java开发者准备的面试指南,包含了广泛而深入的Java技术知识,以及面试过程中可能会遇到的各种问题。这本书的完整版PDF提供了丰富的学习材料,帮助求职者提升自己的技术水平...

    黑马程序员入学面试题

    面向对象更加强调通过对象来封装数据和行为,而面向过程则更多关注于函数和过程。 #### 35. 线程之间通信的理解 线程之间通信是指线程之间如何交换数据或同步执行顺序。常用的机制包括wait()、notify()、join()等。...

    黑马程序员入学Java知识

    ### 黑马程序员入学Java知识 #### Java概述与基础知识 1. **何为编程?** - 编程是通过特定的计算机语言来编写指令,让计算机能够执行一系列任务的过程。 2. **Java语言概述,历史、特点** - Java是一种广泛...

    Java-IO流高级-例题 & 例题源码 & PPT教学文档(黑马程序员详细版).rar

    本资料包“Java-IO流高级-例题 & 例题源码 & PPT教学文档(黑马程序员详细版).rar”提供了一个深入学习Java IO流的全面资源,包含实例题目、源代码以及PPT教学材料,适合对Java IO有进阶需求的开发者。 1. **Java ...

    黑马程序员面试宝典(java)2018版

    《黑马程序员面试宝典(java)2018版》是一本专门为Java开发者准备的面试指南,涵盖了大量在面试过程中可能遇到的问题和知识点。这本宝典由黑马程序员机构精心编纂,汇集了近万名学员的实际面试经验,为求职者提供了...

    黑马程序员入学Java知识——精华总结

    "黑马程序员入学Java知识——精华总结"这份文档很可能包含了Java编程的基础到进阶的知识点,旨在帮助初学者快速掌握Java的核心概念和技能。 1. **Java基础知识**:Java是一种面向对象的语言,它的基础包括语法、...

    黑马程序员Android学习笔记

    《黑马程序员Android学习笔记》是一份专为初学者设计的详尽教程,旨在帮助那些希望踏入安卓开发领域的人员快速掌握核心知识。这份笔记涵盖了从基础到进阶的多个主题,帮助学习者系统地理解Android应用开发的过程。 ...

    黑马程序员_Java基础辅导班教程课件[第01期]第4天

    在"黑马程序员_Java基础辅导班教程课件[第01期]第4天"中,我们可以推测这是针对初学者的Java编程课程,旨在帮助学员掌握Java的基础知识。 在课程的第4天,可能讲解了以下核心概念: 1. **类(Class)**:`Demo.class...

    黑马程序员面试宝典(java).7z

    《黑马程序员面试宝典》是针对Java开发人员的一份综合性的面试准备资料,它涵盖了Java全栈开发中的关键知识点,并且特别强调了在面试中可能会遇到的问题和解答。这份资源包含了一份超过500页的企业面试真题集,旨在...

    黑马程序员Android视频教程

    ### 黑马程序员Android视频教程知识点解析 #### 一、Android基础概述 - **定义与特点**:Android是一种基于Linux内核(不包括GNU组件)的开源操作系统,主要用于移动设备。它由Google公司及其领导下的开放手机联盟...

Global site tag (gtag.js) - Google Analytics