Java内存结构

image.png

程序计数器是jvm执行程序的流水线,存放一些跳转指令,和记录当前线程执行的位置。 本地方法栈是jvm调用操作系统方法所使用的栈。 虚拟机栈是jvm执行java代码所使用的栈。 为了保证线程中的局部变量不被别的线程访问到,虚拟机栈和本地方法栈是线程私有的。 方法区(元空间)存放了一些常量、静态变量、类信息等,可以理解成class文件在内存中的存放位置。 虚拟机堆是jvm执行java代码所使用的堆。

image.png

注:图中方法区的class是.class字节码转换后的运行区数据结构,而不是Class对象

  1. 局部变量和方法参数在jvm中的储存方法是相同的,都是在栈上开辟空间来储存的,随着进入方法开辟,退出方法回收。
  2. 每个线程都分配一个独享的stack,所有线程共享一个heap。对于每个方法的局部变量来说,是绝对无法被其他方法,甚至其他线程的同一方法所访问到。

    值传递和引用传递

    Java 到底是值传递还是引用传递?
    Java是按值传递。参数传递基本上就是赋值操作,基本类型传的本身的值,引用类型传的是堆里的地址(可当做值)。
    我的理解:两个栈空间引用同一地址只能说明他们对应的地址值相同,而不代表他们的引用永远绑定在一起(如果是按引用传递,那么就是这么个情况),只要对其中一个栈空间重新赋值,就会在堆中开辟新的内存地址,引用这个新的地址。
    JavaScript也是按值传递
    1. StringBuilder sb = new StringBuilder("iphone");
    2. void foo(StringBuilder builder) {
    3. builder = new StringBuilder("ipad");
    4. }
    5. foo(sb); // sb 没有被改变,还是 "iphone"
    image.png => image.png
    如果是按引用传递
    image.png => image.png

    常量池

    深入浅出Java常量池
    Java中的常量池:静态常量池(.class文件中)运行时常量池(方法区)
    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。

    字符串常量池

    例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。
    (1)节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
    (2)节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。
  • 字面量、字面量拼接后的结果,会放进静态常量池和运行时常量池
  • 含有new String()拼接变量拼接的字符串会在堆中产生,不会和方法区常量池等值的字符串地址相同

    数值类型常量池

    数值类型的常量池不可以手动添加常量,程序启动时常量池中的常量就已经确定了,比如整型常量池中的常量范围:-128~127,(Byte,Short,Integer,Long,Character,Boolean)这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据(堆中),但是超出此范围仍然会去创建新的对象。例如在自动装箱时,把int变成Integer的时候,首先去缓存中找,找到的话直接返回引用给你就 行了,不必再新new一个),如果不在-128-IntegerCache.high(127) 时会返回一个新new出来的Integer对象。

    gc

    没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收
    查看当前服务器垃圾回收机制
    1. java -XX:+PrintCommandLineFlags -version

    反编译

    jd-gui
    jad 不准