目录
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);
}
}
编译运行:
备注:错误 【错误: 编码GBK的不可映射字符】解决办法
报错原因:windows下默认的字符集为:GBK,而当你的java文件当中的汉字不是字符集:GBK时,javac进行编译的时候就会报错。
解决方法:
(1)javac指定文件编码方式,例如
javac -encoding UTF-8 HelloWorld.java
(2)将文件编码设置成GBK编码