关于JAVA程序的内存分布

目录

1.Java程序运行时内存说明

2.JVM内存划分

3.Java中数据类型

4.Java中的String

5.结合HelloWorld分析java程序内存分布


1.Java程序运行时内存说明

          编写的.java程序文件需要java编译器javac转成.class文件,然后通过jvm(名为java的可执行程序)来加载.class文件执行。每运行一个java程序就会产生一个java(JVM)的实例。一个java进程对应一个JVM实例,该进程可能包含一个或者多个线程,每个JVM实例都有一个对应的堆,每个线程有自己私有的栈。进程创建的所有类的对应本身何数组本身存放在堆上,由进程所有的线程共享。Java中堆上为对象分配内存会初始化这个对象中的变量。堆上对象的引用是再栈中分配,创建一个对象再堆和栈上都分配内存,堆中分配的内存存放对象本身,而在栈中分配的内存呢只是存放指向这个堆对象的引用而已。在函数栈帧中new出来一个局部变量时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。

2.JVM内存划分

JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method,也叫静态区):

堆区: 
(1)存储的全部是对象,每个对象都包含一个与之对应的class的信息(class的目的是得到操作指令) ;
(2)jvm只有一个堆区(heap),且被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身和数组本身;

栈区: 
(1)每个线程包含一个栈区,栈中只保存基础数据类型本身和自定义对象的引用;
(2)每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问;
(3)栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令);

方法区(静态区): 
(1)被所有的线程共享,方法区包含所有的class(class是指类的原始代码,要创建一个类的对象,首先要把该类的代码加载到方法区中,并且初始化)和static变量。 
(2)方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。 
堆(heap)和栈(stack)的区别 :
(1)栈(stack)与堆(heap)都是Java用来存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 
(2)栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。
但缺点是由于要在运行时动态分配内存,存取速度较慢。

3.Java中数据类型

(1)基本类型(primitive types), 共有8类,即int, short, long, byte, float, double, boolean, char。
这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。自动变量存的是字面值,不是类的实例,
即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。
这些字面值的数据,由于大小可知和生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。 
(2)包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。
这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 
(3)自定义数据类型
用new()语句创建,对象存放在在堆区,通过栈区的引用来使用。

4.Java中的String

(1)栈的共享特性
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
说明:
1)编译器先处理String str1 = "abc";它会在栈中创建一个变量为str1的引用,然后查找栈中是否有abc这个值,如果没找到,就将abc存放进来,然后将str1指向abc。
2)接着处理String str2 = "abc";在创建完b的引用变量后,因为在栈中已经有abc这个值,便将str2直接指向abc。这样,就出现了str1与str2同时均指向abc的情况。
所以我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。其实对象可能并没有被创建(在栈上创建),而可能只是指向一个先前已经创建的对象。
(2)堆内存
通过new()方法才能保证每次都创建一个新的对象。其存放在堆上。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,
以提高程序效率。

5.结合HelloWorld分析java程序内存分布

HelloWorld.java

//import java.lang.Integer;

public class HelloWorld {  //运行时jvm 把HelloWorld的代码全部都放入方法区     
    public static void main(String[] args) {  //main方法放在方法区
        System.out.println("Hello World!");
		Student stu = new Student(110, "Andy", 18);  //stu在栈上,引用堆上new出来的对象,new Student(110, "Andy", 18)在堆上存储
		stu.printStudent();
		int[] iArr = new int[10]; //iArr在栈上,引用堆上new出来的对象,new int[10]在堆上存储
		for (int i = 0; i < 10; i++) {
			iArr[i] = i;
		}
		System.out.println("iArr: " + iArr);
		int[] iArr1 = {12, 34, 45, 60, 45, 82};
		System.out.println("iArr: " + iArr1);
    }
}

class Student { //运行时jvm 把Student的代码全部都放入方法区   
private int id;
private String name;
private Integer age;   
    
public Student(int id, String name, Integer age) {     
	this.id = id;
	this.name = name;
	this.age = age;
}     
   
   
public void  printStudent() { //printStudent方法放在方法区
	System.out.println("id: " + id); 
	System.out.println("name: " + name); 
	System.out.println("age: " + age); 
}  
   
}

编译运行:

反编译字节码:

javap -v HelloWorld.class

  备注:错误 【错误: 编码GBK的不可映射字符】解决办法

报错原因:windows下默认的字符集为:GBK,而当你的java文件当中的汉字不是字符集:GBK时,javac进行编译的时候就会报错。

解决方法:

(1)javac指定文件编码方式,例如

javac -encoding UTF-8 HelloWorld.java

(2)将文件编码设置成GBK编码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值