zoukankan      html  css  js  c++  java
  • java内存模型知识点汇总

    1.像windows/linux这种操作系统中,自带jvm么?以方便java程序的运行?

    答:是的,一般操作系统都自带jvm的。但不带jdk,也就是说java的运行环境有,但编译环境没有。

    1.java程序运行时分哪几种内存区域?

    答:大概分为5种内存区域。这5种内存区域,又分为共享区内存,线程私有内存区。

    方法区,堆,线程栈,本地方法区,程序计数器。

    共享区:堆,方法区。

    线程私有区:本地方法区,程序计数器,线程栈。

    2.java虚拟机内存划分不同区域的目的什么?

    答:为了提高运算效率,就对数据进行了不同空间的划分,每一片区域采用特定的处理数据方式和内存管理方式。

    3.java虚拟机都包括哪些知识点?

    答:从虚拟机启动开始算起考虑嘛,包括类加载机制,内存分配(内存模型),垃圾回收。

    所以,java虚拟机就包括三方面知识点,类加载机制,内存分配和垃圾回收。

    2.每种内存区域里存储什么数据?

    各区介绍:

      1)方法区(线程共享):用于存放被虚拟机加载的类的信息(就是类的全部代码),静态变量,常量。

      2)Java堆(线程共享):存放对象实例和数组,这里是内存回收的主要地方。可以分为新生代(young)和年老代(tenured)。从字面也可以知道,新生代存放刚刚建立的对象,而年老代存放长久没有被垃圾回收机制回收的对象。一般新生代有分为eden,from survivor和to survivor。这是和回收算法相关的分配(通过-Xms和-Xmx来配置)。

      3)线程栈(线程私有):随线程一起建立,是方法执行的内存模型。当方法开始执行时,载入局部变量(引用变量),参数,返回值。栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss参数可以设置虚拟机栈大小)。栈的大小可以是固定的,或者是动态扩展的。如果请求的栈深度大于最大可用深度,则抛出stackOverflowError;如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出OutofMemoryError。

      4)程序计数器(线程私有):java被编译成class,并被JVM解释执行,执行的是每一条指令。程序计数器记录当前线程执行的字节码地址,使得程序在轮询获取CPU时间片执行的时候能够知道从何处执行。因此很好想到,这篇区域是线程私有的,因为这里记录的就是不同线程的执行地址。(程序计数器一般都比较小,所以在内存资源的分析时都会忽略这片内存的占用)。

      5)本地方法栈(线程私有):本地方法栈和虚拟机栈类似,只是这是存储本地方法的内存模型,管理本地方法的执行(Native)

    3.java 堆和栈之间是怎样联系的?

    答:new创建对象时,是在堆和栈中都分配内存的。Java中对象的存储空间都是在堆中分配的,但是这对象的引用却是在栈中分配,也就是说在建立一个对象时在堆和栈中都分配内存,在堆中分配的内存实际存放这个被创建的对象的本身,而在栈中分配的内存只是存放指向这个堆对象的引用而已。

    4.方法退出时,怎么样处理栈内存和堆内存?

    答:方法退出时,栈空间立刻被回收,局部变量生命周期立即,堆空间中的刚才方法中创建的对象等待GC回收。

    5.栈内存是什么时候建立的?

    答:程序是由main()方法开始执行的,java程序总是最少两个线程,main()线程和gc垃圾回收线程。所以main线程是立即建立的,同时立即创建main线程的栈内存。

    6.堆,栈,方法区的创建时间是在什么时候创建的?

    答:虚拟机启动时自动创建堆。

    方法区在被虚拟机加载类的时候,载入类信息、常量、静态常量。

    栈是在线程启动时候自动创建。

    7.java虚拟机主要包括哪些知识点?

    答:Java虚拟机知识点包括内存模型和垃圾回收器。

    8.方法区是由什么组成的?

    答:方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。  

    9.线程内存是怎样与主内存交互的?

    答:当线程与内存区域进行交互时,数据从主存拷贝到工作内存,进而交由线程处理(操作码+操作数)。

    Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。

    10.线程栈的具体组成是?

    答:虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame ①)用于存储局部变量表、操作栈、动态

    链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

    局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不等同于对象本身。

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

    3、 JVM的进程中,每个线程都会拥有一个方法调用栈,用来跟踪线程运行中一系列的方法调用过程,栈中的每一个元素就被称为栈帧,每当线程调用一个方法的时候就会向方法栈压入一个新帧。这里的帧用来存储方法的参数、局部变量和运算过程中的临时数据。

    11.java虚拟机垃圾回收机制

    12.程序运行时内存分配情况分析

    答:首先 启动一个Java虚拟机进程,这个进程首先从classpath中找到AppMain.class文件,读取这个文件中的二进制数据,然后把Appmain类的类信息存放到运行时数据区的 方法区 中,这就是AppMain类的加载过程。 

     接着,Java虚拟机定位到方法区中AppMain类的Main()方法的字节码,开始执行它的指令。这个main()方法的第一条语句就是: 

    Sample test1=new Sample("测试1");
    该语句的执行过程: 
        1、 Java虚拟机到方法区找到Sample类的类型信息,没有找到,因为Sample类还没有加载到方法区(这里可以看出,java中的内部类是单独存在的,而且刚开始的时候不会跟随包含类一起被加载,等到要用的时候才被加载)。
    Java虚拟机立马加载Sample类,把Sample类的类型信息存放在方法区里。  
      2、 Java虚拟机首先在堆区中为一个新的Sample实例分配内存, 并在Sample实例的内存中存放一个方法区中存放Sample类的类型信息的内存地址。 
      3、位于“=”前的Test1是一个在main()方法中定义的一个变量(一个Sample对象的引用),因此,它被会添加到了执行main()方法的主线程的JAVA方法调用栈中。
    而“=”将把这个test1变量指向堆区中的Sample实例。 
    4、JVM依次执行它们的printName()方法。当JAVA虚拟机执行test1.printName()方法时,JAVA虚拟机根据局部变量test1持有的引用,定位到堆区中的Sample实例,再根据Sample实例持有的引用,
    定位到方法去中Sample类的类型信息,从而获得printName()方法的字节码,接着执行printName()方法包含的指令,开始执行。
    AppMain.java 
     2  
     3   public   class  AppMain                //运行时, jvm 把appmain的代码全部都放入方法区     
     4   {     
     5   public   static   void  main(String[] args)  //main 方法本身放入方法区。     
     6   {     
     7   Sample test1 = new  Sample( " 测试1 " );   //test1是引用,所以放到栈区里, Sample是自定义对象应该放到堆里面     
     8   Sample test2 = new  Sample( " 测试2 " );     
     9      
    10   test1.printName();     
    11   test2.printName();     
    12   }     
    13   }     
    14      
    15   public   class  Sample        //运行时, jvm 把appmain的信息都放入方法区     
    16   {     
    17   /** 范例名称 */     
    18   private String name;      //new Sample实例后, name 引用放入栈区里, name 对应的 String 对象放入堆里     
    19      
    20   /** 构造方法 */     
    21   public  Sample(String name)     
    22   {     
    23   this .name = name;     
    24   }     
    25      
    26   /** 输出 */     
    27   public   void  printName()   //在没有对象的时候,print方法跟随sample类被放入方法区里。     
    28   {     
    29   System.out.println(name);     
    30   }     
    31   }  

    13.在Java语言里堆(heap)和栈(stack)里的区别 :

        1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。  
    2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享(详见下面的介绍)。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

    Java中的2种数据类型:

    一种是 基本类型 (primitive types), 共有8类,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。 自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在 。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。 这些 字面值 的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存 在于栈中 。  

        栈有一个很重要的特性:存在栈中的数据可以共享。 假设我们同时定义:  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与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。  

    另一种是 包装类数据, 如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部 存在于堆中, Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。 

    15.字面变量的引用和类变量引用有什么区别?

    答:8种基本类型的引用变量叫字面变量引用,8种基本数据类型是存储在栈中的,不是堆中,因为字面变量的大小是已知的,且生命周期是已知的。int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。栈有一个很重要的特性--栈的数据是可以共享的。说的就是字面变量内容是可以共享的。

     假设我们同时定义:  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与 b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。  

    自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在 。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。 这些 字面值 的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存 在于栈中 。  

    16.程序中new创建的对象为什么在程序运行后才根据new命令在堆中创建内存空间?

    答:因为new创建的对象是程序猿写的,大小未知啊。所以编译时期不能创建内存空间,程序实际运行时,才知道啊。new创建的对象不像int这种基本数据类型,在编译时期就知道大小,就可以存储在栈中。

    15.java的内存分配条理清楚么?

    答: java内存分配条理还是很清楚的,如果要彻底搞懂,可以去查阅JVM相关的书籍。

    在java中,内存分配最让人头疼的是String对象,由于其特殊性,所以很多程序员容易搞混淆。

  • 相关阅读:
    团队项目-第一阶段冲刺7
    团队项目-第一阶段冲刺6
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(七) 实用技术篇
    Spring Boot 揭秘与实战(六) 消息队列篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(五) 服务器篇
    Spring Boot 揭秘与实战(四) 配置文件篇
  • 原文地址:https://www.cnblogs.com/panxuejun/p/5889418.html
Copyright © 2011-2022 走看看