从头开始学Java——JVM虚拟机八问

2023-11-10

相关链接:

  1. JavaVM——Java内存区域与内存溢出异常
  2. 为分布式做准备吧——JVM线程资源同步及交互机制
  3. 为分布式做准备吧——深入理解JVM
  4. 从头开始学Java——重新理解Java反射

什么是Java虚拟机,为什么Java被称为“平台无关的编程语言”?

Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件

什么是JIT?

JIT 是 just in time 的缩写, 也就是即时编译编译器。上面我们说过编译的流程, javac 将程序源代码编译,转换成 java 字节码,JVM 通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。这样编译->再翻译的过程,其执行速度肯定会比直接执行二进制字节码程序要慢。所以为了提高执行速度,引入了 JIT 技术,在运行时 JIT 会把翻译过的机器码保存起来,以备下次使用

JVM将重复出现很多次数,循环,重复调用的方法的字节码给保存起来.这种代码,叫热点代码,检测出热点代码的过程叫热点检测(HotSpot)

在这里插入图片描述

HotSpot怎么工作的?

1、基于采样的方式探测(Sample Based Hot Spot Detection) :周期性检测各个线程的栈顶,发现某个方法经常出现在栈顶,就认为是热点方法。好处就是简单,缺点就是无法精确确认一个方法的热度。容易受线程阻塞或别的原因干扰热点探测。

2、基于计数器的热点探测(Counter Based Hot Spot Detection)。采用这种方法的虚拟机会为每个方法,甚至是代码块建立计数器,统计方法的执行次数,某个方法超过阀值就认为是热点方法,触发JIT编译。

HotSpot虚拟机要使用解释器与编译器并存的架构?

尽管并不是所有的Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商用虚拟机(如HotSpot),都同时包含解释器和编译器。解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率
当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。

什么是编译时、运行时?

编译时顾名思义就是正在编译的时候。那什么叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码。(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言。比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL。另外还有链接器,汇编器,为了便于理解我们可以统称为编译器)。

所谓运行时就是代码跑起来了,被装载到内存中去了。(你的代码保存在磁盘上没装入内存之前是个死家伙,只有跑到内存中才变成活的)。而运行时类型检查就与前面讲的编译时类型检查(或者静态类型检查)不一样,不是简单的扫描代码,而是在内存中做些操作,做些判断

编译

  • 方法重载
    方法重载:这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。

  • 泛型
    泛型(又称类型检验):这个是发生在编译期的编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。换句话来说,编译器会擦除所有在尖括号里的类型信息

  • 继承
    继承:发生在编译时,因为它是静态的。

运行

  • 方法覆盖
    方法覆盖:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。

  • 代理或者组合
    代理或者组合:发生在运行时,因为它更加具有动态性和灵活性。

编译时运行时问题归纳

理解了这样两个概念,可以回答下面几个问题:

  1. “static”关键字是什么意思?Java中是否可以覆盖(override) static的方法?
    “static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
    Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关,所以概念上不适用。
  2. 是否可以在static环境中访问非static变量?
    static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没有跟任何实例关联上。
  3. 下面的代码LineA和LineB是在哪个阶段计算的呢?
public class ConstantFolding {

    static final  int number1 = 5;
    static final  int number2 = 6;
    static int number3 = 5;
    static int number4= 6;

    public static void main(String[ ] args) {
          int product1 = number1 * number2;         //line A
          int product2 = number3 * number4;         //line B
          System.out.println(product1);
          System.out.println(product2);
    }
}

常量折叠是一种Java编译器使用的优化技术。由于final变量的值不会改变,因此就可以对它们优化。正确答案是,LineA是在编译时,LineB是在运行时(number3和number4是在编译时被创建,但是计算还是要等到运行时)。
使用在前置文章2中用到的javac,我们可以看到,实际上.class是这样的:

public class ConstantFolding {
    static final int number1 = 5;
    static final int number2 = 6;
    static int number3 = 5;
    static int number4 = 6;

    public ConstantFolding() {
    }

    public static void main(String[] var0) {
        byte var1 = 30;
        int var2 = number3 * number4;
        System.out.println(var1);
        System.out.println(var2);
    }
}

反射

反射也与运行时和编译时有关,所以将在接下来的内容里详细介绍。

描述Java内存模型?

在这里插入图片描述

线程私有

  • 程序计数器

字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java虚拟机的多线程就是通过线程轮流切换并分配处理器时间来实现的,为了线程切换后能恢复到正确的位置,每条线程都需要一个独立的程序计数器。

从上面的描述中,可能会产生程序计数器是否是多余的疑问。因为沿着指令的顺序执行下去,即使是分支跳转这样的流程,跳转到指定的指令处按顺序继续执行是完全能够保证程序的执行顺序的。假设程序永远只有一个线程,这个疑问没有任何问题,也就是说并不需要程序计数器。但实际上程序是通过多个线程协同合作执行的。
首先我们要搞清楚JVM的多线程实现方式。JVM的多线程是通过CPU时间片轮转(即线程轮流切换并分配处理器执行时间)算法来实现的。也就是说,某个线程在执行过程中可能会因为时间片耗尽而被挂起,而另一个线程获取到时间片开始执行。当被挂起的线程重新获取到时间片的时候,它要想从被挂起的地方继续执行,就必须知道它上次执行到哪个位置,在JVM中,通过程序计数器来记录某个线程的字节码执行位置。因此,程序计数器是具备线程隔离的特性,也就是说,每个线程工作时都有属于自己的独立计数器。

为什么程序计数器没有规定OutOfMemoryError
程序计数器存储的是字节码文件的行号,而这个范围是可知晓的,在一开始分配内存时就可以分配一个绝对不会溢出的内存。

执行native本地方法时,程序计数器的值为空(Undefined)。因为native方法是java通过JNI直接调用本地C/C++库,可以近似的认为native方法相当于C/C++暴露给java的一个接口,java通过调用这个接口从而调用到C/C++方法。由于该方法是通过C/C++而不是java进行实现。那么自然无法产生相应的字节码,并且C/C++执行时的内存分配是由自己语言决定的,而不是由JVM决定的。
在这里插入图片描述


  • Java虚拟机栈

Java虚拟机栈描述的是Java方法执行的内存模型,每个方法执行的同时会创建一个栈帧。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程

局部变量表:方法内部的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,指向堆区)。

局部变量存储在局部变量表中,随着线程而生,线程而灭。并且线程间数据不共享。
但是,如果是成员变量,或者定义在方法外对象的引用,它们存储在堆中。

操作数栈:操作数栈可理解为java虚拟机栈中的一个用于计算的临时数据存储区
动态连接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。

方法出口:当一个方法开始执行后,只有2种方式可以退出这个方法 :

  • 方法返回指令 : 执行引擎遇到一个方法返回的字节码指令,这时候有可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口。
  • 异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出。

无论采用任何退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息。一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中会保存这个计数器值。
    
--------------------------------------
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

动态扩展就是在栈空间不够的时候,自动加大栈的空间,避免StackOverflow,JVM是没有实现这个功能的。
动态栈通常有两种方法:Segmented stack和Stack copying。
Segmented stack可以简单理解成一个双向链表把多个栈连接起来,一开始只分配一个栈,这个栈的空间不够时,就再分配一个,用链表一个一个连起来。
Stack copying就是在栈不够的时候,分配一个更大的栈,然后把原来的栈复制过去。

如下:

public class TestStackOverflowErrorDemo {
    //栈深度统计值
    private int stackLength = 1;

    /**
     * 递归方法,导致栈深度过大异常
     */
    public void stackLeak() {
        stackLength++;
        stackLeak();
    }

    /**
     * 启动方法
     * 测试结果:当-Xss 180k为180k时,stackLength~=1544,随着-Xss参数变大时stackLength值随之变大
     * @param args
     */
    public static void main(String[] args) {
        TestStackOverflowErrorDemo demo = new TestStackOverflowErrorDemo();
        try {
            demo.stackLeak();
        } catch (Throwable e) {
            System.out.println("当前栈深度:stackLength=" + demo.stackLength);
            e.printStackTrace();
        }
    }
}

在这里插入图片描述


  • 本地方法栈

跟虚拟机栈很像,不过它是为虚拟机使用到的Native方法服务。

线程共享

  • Java堆

堆是 JVM 所管理的最大的一块内存空间,对象实例数组几乎都在这分配内存。
堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。堆也就是GC 收集垃圾的主要区域。

另外,堆区还包含了一个DirectByteBuffer对象,指向了堆外内存。
在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式。它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆上的的 DirectByteBuffer对象 作为这块内存的引用进行操作。这样避免了在Java堆和Native堆中来回复制数据,从而提高性能

在这里插入图片描述


  • 方法区

各个线程共享的区域,主要存储加载的类字节码、class/method/field等元数据对象、static-final常量和static变量、jit编译器编译后的代码等数据

(1)加载的类字节码:要使用一个类,首先需要将其字节码加载到JVM的内存中。至于类的字节码来源,可以多种多样,如.class文件、网络传输、或cglib字节码框架直接生成。
(2)class/method/field等元数据对象:字节码加载之后,JVM会根据其中的内容,为这个类生成Class/Method/Field等对象,它们用于描述一个类,通常在反射中用的比较多。不同于存储在堆中的java实例对象,这两种对象存储在方法区中。
(3)static-final常量、static变量:对于这两种类型的类成员,JVM会在方法区为它们创建一份数据,因此同一个类的static修饰的类成员只有一份;
(4)jit编译器的编译结果:以hotspot虚拟机为例,其在运行时会使用JIT即时编译器对热点代码进行优化,优化方式为将字节码编译成机器码。通常情况下,JVM使用“解释执行”的方式执行字节码,即JVM在读取到一个字节码指令时,会将其按照预先定好的规则执行栈操作,而栈操作会进一步映射为底层的机器操作;通过JIT编译后,执行的机器码会直接和底层机器打交道。

方法区:JDK8之前,由永久代实现,主要存放类的信息、常量池、方法数据、方法代码等;JDK8之后,取消了永久代,提出了元空间,并且常量池、静态成员变量等迁移到了堆中

整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,其他的虚拟机实现并没有永久代这一说法。


  • 运行时常量池

运行时常量池是方法区的一部分。具体参考:Java常量池详解:字符串常量池、Class常量池、运行时常量池 三者关系

java中垃圾收集的方法有哪些?

标记-清除(Mark-Sweep)

这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:

  1. 效率不高,标记和清除的效率都很低;
  2. 会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。

标记-整理

该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。
内存单元并不会在变成垃圾立刻回收,而是保持不可达状态,直到到达某个阈值或者固定时间长度。这个时候系统会挂起用户程序,也就是 STW,转而执行垃圾回收程序。

复制算法

为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。

但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。 于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存交Eden区,其余是两块较小的内存区叫Survivor from区和Survivor to区

  1. 首先,把Eden和ServivorFrom区域中存活的对象复制到ServicorTo区域(如果有对象的年龄以及达到了老年的标准,一般是15,则赋值到老年代区
  2. 同时把这些对象的年龄+1(如果ServicorTo不够位置了就放到老年区
  3. 然后,清空Eden和ServicorFrom中的对象;最后,ServicorTo和ServicorFrom互换,原ServicorTo成为下一次GC时的ServicorFrom区。

大对象直接进入老年代。大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。

在这里插入图片描述
在这里插入图片描述

那为什么需要两个Survivor区呢,因为复制后Survivor from区虽然现在很整齐,没有碎片,当下一次进行回收时,Eden区和Survivor from区里都存在需要回收的对象,则Survivor from区也会出现碎片。

分代收集

现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。

新生代、老年代、永久代?

Java堆内存:新生代+老年代。
方法区:永久代。
在这里插入图片描述

新生代

主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收。

新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。

  • Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
  • ServivorTo:保留了一次MinorGC过程中的幸存者。
  • ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

当JVM无法为新建对象分配内存空间的时候(Eden满了),Minor GC被触发

老年代

老年代的对象比较稳定,所以MajorGC不会频繁执行。

在进行MajorGC前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。

MajorGC采用标记-清除算法:

  1. 首先扫描一次所有老年代,标记出存活的对象
  2. 然后回收没有标记的对象。

MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常

永久代

很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低。

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“abc”常量就会被系统“请”出常量池。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

元空间与永久代之间最大的区别在于元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入 native memory, 字符串池和类的静态变量放入java堆中. 这样可以加载多少类的元数据就不再由MaxPermSize控制, 而由系统的实际可用空间来控制

如何判断一个对象是否存活?(或者GC对象的判定方法)

判断一个对象是否存活有两种方法:(一般就是下面两个流派)

  • 引用计数法

所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收。

优点:

  1. 渐进式。内存管理与用户程序的执行交织在一起,将 GC 的代价分散到整个程序。不像标记-清扫算法需要 STW (Stop The World,GC 的时候挂起用户程序)。
  2. 内存单元能够很快被回收。相比于其他垃圾回收算法,堆被耗尽或者达到某个阈值才会进行垃圾回收。

缺点:

  1. 原始的引用计数不能处理循环引用(不过针对这个问题,也除了很多解决方案,比如强引用等)。
  2. 维护引用计数降低运行效率。内存单元的更新删除等都需要维护相关的内存单元的引用计数,相比于一些追踪式的垃圾回收算法并不需要这些代价。
  3. 单元池 free list 实现的话不是 cache-friendly 的,这样会导致频繁的 cache miss,降低程序运行效率。
  • 可达性算法(引用链法)

该算法的思想是:从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。

在java中可以作为GC Roots的对象有以下几种:
(1)虚拟机栈中引用的对象

void test() {
  B b = new B(); // 引用对象b
}

(2)方法区类静态属性引用的对象

public class B {
  private static A a; // 类静态属性引用对象
}

(3)方法区常量池引用的对象

public class B {
  private static final A a; // 类静态属性引用对象
}

(4)本地方法栈JNI引用的对象

void test() {
  JNI引用对象
}

当一个对象不可达GC Root时,这个对象并不会立马被回收,而是出于一个死缓的阶段,若要被真正的回收需要经历两次标记如果对象在可达性分析中没有与GC Root的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。

如果该对象有必要执行finalize()方法,那么这个对象将会放在一个称为F-Queue的对队列中,虚拟机会触发一个Finalize()线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果finalize()执行缓慢或者发生了死锁,那么就会造成F-Queue队列一直等待,造成了内存回收系统的崩溃。GC对处于F-Queue中的对象进行第二次被标记,这时,该对象将被移除”即将回收”集合,等待回收。

Minor GC、Major GC和Full GC之间的区别?

三者负责的区域:

  • Minor GC 是 清理 Eden区 ;
  • Major GC 是 清理 老年代 ;
  • Full GC 是 清理整个堆空间,包括 年轻代和老年代(和永久代)。

Major GC通常是跟full GC是等价的,收集整个GC堆。但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的full GC还是old GC。

触发机制:

  • Minor GC
    当年轻代(Eden区)满时就会触发 Minor GC,这里的年轻代满指的是 Eden区满。
    Survivor 满不会引发 GC 。
  • Full GC
    当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代 ;
    当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载 。
    (1)调用System.gc时,系统建议执行Full GC,但是不一定会执行 。
    (2)老年代空间不足
    (3)方法区空间不足
    (4)通过 Minor GC 后进入老年代的空间大于老年代的可用内存
    (5)由Eden区、survivor space1(From Space)区向survivor space2(To Space)区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小 。

Java GC是在什么时候,对什么东西,做了什么事情?

在什么时候:
参考上一个问题。

对什么东西:
从gc root引用链上开始搜索找不到的对象,而且finalize()第一次标记之后仍然没有复活的对象。

做了什么事情:
回答:年轻代做的是复制清理、from survivor、to survivor是干啥用的、年老代做的是标记清理、标记清理后碎片要不要整理、复制清理和标记清理有有什么优劣势。

GC中Stop the world?

为了更好的理解GC中的Stop the world案例,就必须先了解何为Stop the World方式。所谓的Stop the World机制,简称STW,即在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集收集器线程之外的线程都被挂起
此时,系统只能允许GC线程进行运行,其他线程则会全部暂停,等待GC线程执行完毕后才能再次运行。这些工作都是由虚拟机在后台自动发起和自动完成的,是在用户不可见的情况下把用户正常工作的线程全部停下来,这对于很多的应用程序,尤其是那些对于实时性要求很高的程序来说是难以接受的。但是有些时候对于虚拟机来说采用Stop the world机制是无法避免的,例如采用复制算法时,为了保证在复制存活的对象的时候,对象的一致性,不然要使应用程序被挂起
但是随着java虚拟机的发展,HotSpot虚拟机团队为达到更好用户体验而一直进行着努力,不断的对垃圾收集器进行着改进,随着JDK的版本的不断更新,更好的垃圾收集器的出现,用户线程的停顿时间也在不断缩短,虽然这一时间现阶段仍然不能消除,但相信不久的未来一定会有更好的垃圾收集器被发现,从而完全达到用户对于虚拟机垃圾回收的性能要求。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从头开始学Java——JVM虚拟机八问 的相关文章

  • 如果计算的哈希码超过整数最大限制,会发生什么?

    这是 Java HashTable 类的 hashCode 实现 如果哈希表中的元素数量很大并且哈希码超过 INTEGER MAX LIMIT 2 147 483 648 到 2 147 483 647 该怎么办 我假设 hashCodes
  • 从文件中读取文本并将每行中的每个单词存储到单独的变量中

    我有一个包含以下内容的 txt 文件 1 1111 47 2 2222 92 3 3333 81 我想逐行读取并将每个单词存储到不同的变量中 例如 当我读取第一行 1 1111 47 时 我想将第一个单词 1 存储到var 1 1111 进
  • 在 Java 中重新抛出异常而不丢失堆栈跟踪

    在 C 中 我可以使用throw 语句重新抛出异常 同时保留堆栈跟踪 try catch Exception e if e is FooException throw Java中有没有类似的东西 不会丢失原始堆栈跟踪 catch Whate
  • 用Java截取网页的屏幕截图[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有没有一个免费的工具可以读取给定的网页并截取它的屏幕截图 我使用 VirtualFramebuffer 和 Firefox Binary
  • Java 密钥库 - 以编程方式从密钥库文件中选择要使用的证书

    我有一个 java 密钥库文件 其中包含多个客户端证书 我希望在 Java 应用程序中仅选择其中一个证书来连接到服务 有没有一种简单的方法可以做到这一点 到目前为止 我找到解决方案的唯一方法是使用原始密钥库文件中的客户端证书详细信息 通过其
  • 如何获取JavaFX的版本号?

    如何在运行时找出我正在使用哪个版本的 JavaFX 简单的方法之一就是简单地阅读javafx properties文件位于您的 JAVA HOME jre lib目录 我现在安装了 Java 1 7 u9 与之捆绑的 JavaFX 是 v2
  • Appengine - 隐藏文件夹的部署

    为了验证 SSL 证书 我需要将包含一些文件的隐藏文件夹 well known 上传到我的应用程序 我正在使用 eclipse 部署 java 应用程序 但 appengine 上的应用程序未收到这些文件 我猜他们被过滤掉了 我尝试将隐藏文
  • 规范路径和绝对路径有什么区别? [复制]

    这个问题在这里已经有答案了 可能的重复 Java 中的 getPath getAbsolutePath 和 getCanonicalPath 有什么区别 https stackoverflow com questions 1099300 w
  • 如何在Netbeans中自定义jTable标题列字体大小?

    我尝试改变jtable标题字体大小Netbeans 但还不能 无论如何 表行字体大小已成功更改 这是我使用的方法 更改后的输出 问题 标题字体大小未更改 但我也想改变这一点 所以请帮助我该怎么做 一种方法是使用UIManager并替换默认的
  • Java RCP/SWT - Eclipse RCP 中的“Android Toast like”对话框

    有谁知道是否存在某些弹出窗口的实现 例如 Android TOAST 通知是以下内容的一部分迈林公共区 https projects eclipse org projects mylyn commons 要集成它们 请添加Mylyn Com
  • 使用 IntelliJ 调试 Java 进程 - 连接到套接字但不连接到目标 VM

    现在已解决 请参阅问题末尾 我正在尝试使用 IntelliJ Community Edition 的调试器来调试 Java 进程 套接字正在侦听 但是当我尝试连接时 调试过程显示以下内容 连接到目标虚拟机 地址 8003 传输 socket
  • Java 线程 JavaDoc

    我编写了一个只能在特定线程上调用的方法 是否应该将标准注释或注释添加到方法的 javadoc 中来表示这一点 不知道有任何这样的标准注释 Java 并发实践 http www javaconcurrencyinpractice com 在第
  • ImageIO read() 和 write() 操作后 GIF 图像变得错误

    我有这个代码 它只是读取 GIF 文件 用背景重新绘制它 然后输出到新的 GIF 文件 问题是结果文件变得奇怪 我不知道为什么它的质量变得很差 JPG 文件不会出现此问题 如何修复它 import java awt Color import
  • 有没有办法使用 SauceLabs 的 RemoteWebDriver 禁用 CORS 检查

    问题说明了一切 我正在尝试在 SauceLabs 上执行一些硒测试 该测试加载一个发出跨域请求的网页 我在想是否有一种方法可以通过代码以独立于平台的方式禁用 CORS 使用时Chrome驱动程序 Chrome组合禁用cors questio
  • Eclipse 错误:“设置构建路径”遇到错误

    我正在使用一个名为 jtwitter 的 API 它有一个 jar 文件 jtwitter jar 我一直在使用它并使用 git 维护它 我把代码托管在github上 有些天 我没有碰过它的代码 今天 当我克隆我的 git repo 时 实
  • java.lang.Object#getClass() 的 Eclipse 外部空注释

    我正在使用 Eclipse Mars 中提供的外部空注释工具 我正在尝试添加外部注释java lang Object getClass 但似乎无法正确签名 我尝试过以下变体 NonNull Class getClass L1java lan
  • 为什么在 this 方法中添加 If 语句会大大降低速度?

    我在中遇到过这个回答另一个问题 https stackoverflow com questions 12233594 faster way to apply alpha to a jpeg in an android app 我试图诊断哪些
  • 所有语言中特殊字符的 Java 正则表达式

    在我的用户输入字段中 我想允许某些特殊字符 字母和数字的组合 我应该确保正则表达式模式在输入时允许此设置任何语言 基本上我构建的这个正则表达式也应该支持 unicode 表示 如何使用 Java 中的 Pattern 类来实现这一点 这里给
  • Java 中的下载管理器 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要通过 FTP HTTP 从 Java 获取几个大文件 几个演出 有现成的库 java 命令行工具
  • 如何将 Hibernate 5 安装到 Apache Karaf v4 中

    我已经安装了 Apache Karaf v4 03 并查询了 Hibernate 的可用功能列表 如下所示 不幸的是 我使用的是 Hibernate v5 hibernate 3 3 2 GA Uninstalled enterprise

随机推荐

  • 编译SSH代码时,报错configure:error:*** working libcrypto not found,check config.log的原因分析及解决方案

    在将ssh移植到龙芯1B核心板的过程中 当编译openssh 8 0p1工具时 出现了 configure error working libcrypto not found check config log 的报错 根据提示 在opens
  • 目前为止最全的微信小程序项目实例

    wx gesture lock 微信小程序的手势密码 WXCustomSwitch 微信小程序自定义 Switch 组件模板 WeixinAppBdNovel 微信小程序demo 百度小说搜索 shitoujiandaobu 小程序 石头剪
  • BeanUtils.copyProperties,忽略目标对象中不为空的字段

    方法 copyProperties Object source Object target String ignoreProperties 要求 复制对象时 目标对象中不为空的数据 使用 BeanUtils copyProperties s
  • jsp页面中JSTL/EL标签引用java后台静态static字段的方法总结

    为什么使用该功能 项目中的每个页面都包含产品名称 Logo 版本等信息 我希望修改一处 其它所有的全部跟着变 有同学会说那就都引用一个页面 就Ok了 但是我希望这些信息都是可以通过后台代码修改的 修改后保存到数据库和一个静态类中 其实后台直
  • [499]openstack swift 的UI客户端

    了解一下cloudyberry提供的openstack swift客户端 分为收费版和免费版 主要试用了一下免费版 做的还是蛮精致的 很大程度上方便了我们上传 下载 浏览swift上的文件 非常好用 在这里推荐一下 cloudyberry下
  • 华为OD机试 - 斗地主之顺子(Java)

    题目描述 在斗地主扑克牌游戏中 扑克牌由小到大的顺序为 3 4 5 6 7 8 9 10 J Q K A 2 玩家可以出的扑克牌阵型有 单张 对子 顺子 飞机 炸弹等 其中顺子的出牌规则为 由至少5张由小到大连续递增的扑克牌组成 且不能包含
  • 每日一道面试题之介绍一下4+1视图模型!

    4 1视图模型是一种用于软件系统设计和开发的模型 它由4个逻辑视图和一个场景视图组成 每个视图都关注系统的不同方面 为的就是尽可能实现一个全面的系统设计 逻辑视图 描述了软件系统的功能和业务逻辑 它包括了系统的结构和组件之间的关系 以及它们
  • 二叉树的中序遍历(C语言)

    我们从两个方向讲解二叉树的中序遍历 递归 迭代 一 递归 思想 从根节点开始向其的左孩子遍历 一直访问每个节点的左孩子 当其走到NULL时返回 返回时记录每个节点的数值 然后访问该节点的右孩子 如果为NULL直接返回上一层 如果不为NULL
  • Twins: Revisiting the Design of Spatial Attention inVision Transformers解读

    文章 https arxiv org abs 2104 13840 代码 GitHub Meituan AutoML Twins Two simple and effective designs of vision transformer
  • Protobuf Java (2)

    接上一篇文章 Protobuf Java 1 接下来写一个demo 使用protobuf 读写消息 目录 1 写消息 2 读一个消息 3 扩展Protocol Buffer 1 写消息 现在让我们尝试使用协议缓冲区类 您希望地址簿应用程序能
  • CentOS7主机名的查看和修改

    CentOS7主机名的查看和修改 在CentOS7中 有三种定义的主机名 静态的 Static hostname 静态 主机名也称为内核主机名 是系统在启动时从 etc hostname自动初始化的主机名 瞬态的 Tansient host
  • Ping 命令

    PING Packet Internet Groper 因特网包探索器 Ping命令是Windows系列自带的一个可执行命令 利用它可以检查网络是否能够连通 并且能够帮助我们分析判定网络故障 ping的发送和接收 同一个子网中的源主机对目的
  • html ui组件,UI组件

    Bootstrap 天然响应式 12分栏 cnpm install bootstrap 安装相关包 在index html中引入文件后才可以用 如下 ElementUI 24分栏 elementUI使用 安装 element ui cnpm
  • Django 启动报错 mysqlclient 1.4.0 or newer is required; you have 0.9.3

    报错原因 MySQLclient 目前只支持到 Python3 4 这里使用了更高版本的 python 那么需要 我们在Django 配置文件目录下 也就是setting py 同级目录下 配置指定版本的mysqlclient pymysq
  • Flowable工作流引擎的使用2(BPMN结构及节点介绍)

    Flowable工作流引擎的使用 2BPMN结构介绍 上一篇讲到了flowable如何使用 用了一个简单的demo 演示了一下流程的创建 发起 审核 查询等功能 内容不多但是引申出很多的概念 BPMN deployId processId
  • 数据分析笔记—数据仓库篇

    数据仓库 数据仓库 Data Warehouse 可简写为DW或DWH 数仓等 它仅适用于查询和分析 通常涉及大量的历史数据 数据仓库中的数据一般来自应用日志文件 数据埋点 和事务应用 实际发生的业务记录的数据 等广泛来源 一个数据仓库通常
  • yolo 推理 nms

    测试代码 另外一个说明cv2绘制不了中文 但可以用其他包实现 from pathlib import Path import cv2 import torch from models common import DetectMultiBac
  • 数组新增的常用方法(es6-es12)-今天一定要学会

    1 forEach 遍历数组中的元素 不改变原数组 2 map 遍历数组 对数组中每个元素做操作并将操作后的元素放到数组中返回 不改变原数组 3 filter 过滤 返回包含所有在回调函数上结果未true的值的新数组 不改变原数组 4 ev
  • 关于CPU的浮点运算能力计算

    原文链接 https www jianshu com p b9d7126b08cc Intel官方参数 https ark intel com FLOAS 核数 单核主频 CPU单个周期浮点计算值
  • 从头开始学Java——JVM虚拟机八问

    文章目录 什么是Java虚拟机 为什么Java被称为 平台无关的编程语言 什么是JIT HotSpot怎么工作的 HotSpot虚拟机要使用解释器与编译器并存的架构 什么是编译时 运行时 编译 运行 编译时运行时问题归纳 反射 描述Java