jvm内存分配,OS的内存和磁盘,堆栈优缺点对比,常量池

为什么需要jvm,jvm的作用

jvm:Java Virtual Machine,即java虚拟机,来模拟通用的计算机,有着一套虚拟的完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。 JVM是一种规定好的标准规范, 定义了.class文件在其内部运行的相关标准和规范。有了jvm才能执行.class文件,java程序才能正常执行。

jvm内存划分及存储的内容

共分为6个部分:
线程安全(某线程单独占有)的包括:
1)程序计数器:记录程序执行到哪里
2)虚拟机栈:局部变量,对象的引用,基本类型的变量,程序方法执行入口,函数调用的现场等
3)本地方法区:java的补充功能,可能不是使用java编写的程序与功能
非线程安全:
4)堆:new出来的对象实例
5)方法区/静态区:类信息,类变量,静态变量

6)jvm堆外内存:直接内存 + codeCache + metaspace等

Java中定义的基本数据类型存放在堆还是栈中?

在方法内部里面定义的基本数据类型变量,即局部变量,存放在栈中;在类里面定义的基本数据类型变量变量,即全局变量,存放在堆中。

字符串常量池存在于哪里?

jdk7之前字符串常量池存在于方法区,即静态区(方法区)。
jdk7开始常量池已经移到堆中了
,即即常量池和对象共享堆内存。
string.intern方法:如果常量池中已经有数据,则直接从常量池中获取,否则把当前的常量加入常量池中,并返回该常量的引用

操作系统的内存和磁盘

     电脑是靠CPU处理数据而正常工作的,所有的数据都是储存在硬盘上的,但在现实中,CPU的工作频率是相当高的,而硬盘的工作效率则相对要低很多,所以,如果CPU直接从硬盘上来读取数据进行处理的话,硬盘会跟不上CPU的节奏,从而造成CPU资源的闲置。比如硬盘读取某个数据需要用1秒钟,而CPU处理完这个数据仅需要千分之一秒,然后用剩下的时间等待硬盘读取下一个数据。整个过程都耗在了硬盘读取数据上了,CPU实际处理这些数据的时间少之又少,大部分时间则是在无谓的等待,这显然造成了极大的资源浪费。为了解决这个问题,内存就应运而生了。
       内存也是一种存储介质,但它读取数据的效率要比硬盘高的多,比较好的适应了CPU的需要,从而提高了电脑的运行能力。
有了硬盘和内存,电脑的工作过程是这样的:所有的数据依然还是存储在硬盘上,但一些正在使用的数据或者使用率特别高的数据就根据需要被拷贝到了内存中,CPU处理数据时直接从内存中调取,而不再经过硬盘。
       既然内存这么好,那么还要硬盘干什么?内存固然工作效率很出色,但由于技术复杂造价昂贵,所以没办法达到硬盘那么大的存储容量,只能负责一些临时的存储,所以,硬盘也是内存所无法取代的。
      最后还是举个那个经典的例子吧。CPU象个车间,负责生产加工,硬盘是原料仓库,它十分庞大,能储存大量原料,但它离车间很远,来回运输十分不便。这时就在CPU附近又建了个临时中转站,它就是内存。他虽然没有仓库大,但他离车间近,它里面存放着车间急需的原料,提高了生产效率。

jvm中内存分配策略及堆和栈的比较 

1 jvm内存分配策略 

       静态存储分配:也可称为栈式存储分配,是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求. 
  动态存储分配:也可称为堆式存储分配,是由一个类似于堆来实现的.和静态存储分配相反,在堆式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存. 而这个"大小"是在编译时确定的,不是在运行时. 
  静态存储分配要求在编译时能知道所有变量的存储要求,而动态存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放. 

  2 堆和栈的效率比较 
  在编程中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.

       堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。

  3 JVM中的堆和栈
  每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 

  4堆和栈中的垃圾回收与清理
  当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 
  在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 

  5堆和栈的优缺点比较 
堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。 

  栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。 

  栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义: 
  int a = 3; 
  int b = 3; 
  编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量 


面试题:

1String s = new String("xyz");创建了几个字符串对象? 
答:两个对象,一个是静态区(即方法区)的"xyz",一个是用new创建在堆上的对象,还有一个引用放在栈上。
对于上面使用new创建的字符串对象,如果想将这个对象的引用加入到字符串常量池,可以使用intern方法
String  s2 =s.intern();
public String intern()返回字符串对象的规范化表示形式。 
一个初始为空的字符串池,它由类 String 私有地维护。 
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。


2、解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法。 
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的对象放在堆空间;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在静态区中。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。
String str = new String("hello");
上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量放在静态区。
静态区就是方法区.

对于:String str1 = "droid";
JVM检测这个字面量,这里我们认为没有内容为droid的对象存在。JVM通过字符串常量池查找不到内容为droid的字符串对象存在,那么会创建这个字符串对象,然后将刚创建的对象的引用放入到字符串常量池中,并且将引用返回给变量str1。
如果接下来有这样一段代码
复制代码 代码如下:
String str2 = "droid";
同样JVM还是要检测这个字面量,JVM通过查找字符串常量池,发现内容为”droid”字符串对象存在,于是将已经存在的字符串对象的引用返回给变量str2。注意这里不会重新创建新的字符串对象。

3 java对象是不是都是创建在堆上呢?
有的认为,通过逃逸分析,JVM会在栈上分配那些不会逃逸的对象,理论上是可行的,但hotspot中并没有这样做。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值