<JVM上篇:内存与垃圾回收篇>05-本地方法接口和本地方法栈 | 06-堆(一)

简介: <JVM上篇:内存与垃圾回收篇>05-本地方法接口和本地方法栈 | 06-堆

5. 本地方法接口和本地方法栈

5.1. 什么是本地方法?


简单地讲,一个 Native Method 是一个 Java 调用非 Java 代码的接囗。一个 Native Method 是这样一个 Java 方法:该方法的实现由非 Java 语言实现,比如 C。这个特征并非 Java 所特有,很多其它的编程语言都有这一机制,比如在 C++中,你可以用 extern “c” 告知 c++编译器去调用一个 c 的函数。


A native method is a Java method whose implementation is provided by non-java code.


在定义一个 native method 时,并不提供实现体(有些像定义一个 Java interface),因为其实现体是由非 java 语言在外面实现的。


本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++程序。


63a6b100456728af6707d513088c9d62.jpg


举例


public class IHaveNatives {
    //native与abstract的区别?
    /**
     * native:表示有方法体,只不过方法体不是JAVA语言编写的
     * abstract:没有方法体.
     * 所以这两个修饰符不能够同时使用.
     */
    public native void Native1(int x);
    public native static long Native2();
    private native synchronized float Native3(Object o);
    native void Native4(int[] ary) throws Exception;
}


标识符 native 可以与其它 java 标识符连用,但是 abstract 除外


5.2. 为什么使用 Native Method?


Java 使用起来非常方便,然而有些层次的任务用 Java 实现起来不容易,或者我们对程序的效率很在意时,问题就来了。


与 Java 环境的交互


有时 Java 应用需要与 Java 外面的环境交互,这是本地方法存在的主要原因。你可以想想 Java 需要与一些底层系统,如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解 Java 应用之外的繁琐的细节。


与操作系统的交互


JVM 支持着 Java 语言本身和运行时库,它是 Java 程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎样,它毕竟不是一个完整的系统,它经常依赖于一底层系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用 Java 实现了 jre 的与底层系统的交互,甚至 JVM 的一些部分就是用 c 写的。还有,如果我们要使用一些 Java 语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。


Sun’s Java


Sun 的解释器是用 C 实现的,这使得它能像一些普通的 C 一样与外部交互。jre 大部分是用 Java 实现的,它也通过一些本地方法与外界交互。例如:类 java.lang.Thread 的 setPriority()方法是用 Java 实现的,但是它实现调用的是该类里的本地方法 setPriority()。这个本地方法是用 C 实现的,并被植入 JVM 内部,在 Windows 95 的平台上,这个本地方法最终将调用 Win32 setPriority() ApI。这是一个本地方法的具体实现由 JVM 直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被 JVM 调用。


2918c7447f2a15f7ef480a5f8f101efa.png


现状


目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过 Java 程序驱动打印机或者 Java 系统管理生产设备,在企业级应用中已经比较少见。因为现在的异构领域间的通信很发达,比如可以使用 Socket 通信,也可以使用 Web Service 等等,不多做介绍。


5.2. 本地方法栈


Java 虚拟机栈于管理 Java 方法的调用,而本地方法栈用于管理本地方法的调用。


本地方法栈,也是线程私有的。


允许被实现成固定或者是可动态扩展的内存大小。(在内存溢出方面和Java虚拟机栈是相同的)


如果线程请求分配的栈容量超过本地方法栈允许的最大容量,Java 虚拟机将会抛出一个 StackOverflowError 异常。

如果本地方法栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的本地方法栈,那么 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。

本地方法是使用 C 语言实现的。(Java语言调用实现功能扩展.)


它的具体做法是 Native Method Stack 中登记 native 方法,在 Execution Engine 执行时加载本地方法库。


7b30a0a39f9165019101b49d9e8b0f74.jpg


当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。它和虚拟机拥有同样的权限。


本地方法可以通过本地方法接口来访问虚拟机内部的运行时数据区。

它甚至可以直接使用本地处理器中的寄存器

直接从本地内存的堆中分配任意数量的内存。

并不是所有的 JVM 都支持本地方法。因为 Java 虚拟机规范并没有明确要求本地方法栈的使用语言、具体实现方式、数据结构等。如果 JVM 产品不打算支持 native 方法,也可以无需实现本地方法栈。


在 Hotspot JVM 中,直接将本地方法栈和虚拟机栈合二为一。


6. 堆


6.1. 堆(Heap)的核心概述


堆针对一个 JVM 进程来说是唯一的,也就是一个进程只有一个 JVM,但是进程包含多个线程,他们是共享同一堆空间的。


一句话:一个进程对应一个JVM实例,一个进程包含多个线程.一个进程中的多个线程共享堆空间,方法区.每个线程各自有一套自己的程序计数器,本地方法栈,虚拟机栈.


8d825fd68a99daefd85806a297f5aee1.png


一个 JVM 实例只存在一个堆内存,堆也是 Java 内存管理的核心区域。


Java 堆区在 JVM 启动的时候即被创建,其空间大小也就确定了。是 JVM 管理的最大一块内存空间。


堆内存的大小是可以调节的。

代码演示:

/**
 * -Xms10m -Xmx10m:初始堆空间和最大堆空间设置都为10M
 * @author shkstart  shkstart@126.com
 * @create 2020  16:41
 */
public class HeapDemo {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end...");
    }
}
/**
 * -Xms20m -Xmx20m
 * @author shkstart  shkstart@126.com
 * @create 2020  16:42
 */
public class HeapDemo1 {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end...");
    }
}

使用Visual GC观察堆的大小


cee046673392f1238b3566466bcd3a3b.jpg


38dca0f91de38ac5358685a11fa07771.jpg


《Java 虚拟机规范》规定,堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的。


所有的线程共享 Java 堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)(可以更好的解决并发)。


《Java 虚拟机规范》中对 Java 堆的描述是:所有的对象实例以及数组都应当在运行时分配在堆上。(The heap is the run-time data area from which memory for all class instances and arrays is allocated)


从实际角度看:"几乎"所有的对象实例都在这里分配内存(出现逃逸分析,标量分配 会在栈上分配)

数组和对象可能永远不会存储在栈上,因为栈帧中保存引用,这个引用指向对象或者数组在堆中的位置。


在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。(频繁的GC影响用户线程的执行)


堆,是 GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域。


b035c4210e72e55ca25491db7da382b2.png


6.1.1. 堆内存细分


Java 7 及之前堆内存逻辑上分为三部分:新生区+养老区+永久区


Young Generation Space 新生区 Young/New 又被划分为 Eden 区和 Survivor 区

Tenure generation space 养老区 Old/Tenure

Permanent Space 永久区 Perm

Java 8 及之后堆内存逻辑上分为三部分:新生区+养老区+元空间


Young Generation Space 新生区 Young/New 又被划分为 Eden 区和 Survivor 区

Tenure generation space 养老区 Old/Tenure

Meta Space 元空间 Meta

约定:新生区(代)<=>年轻代 、 养老区<=>老年区(代)、 永久区<=>永久代


图解


6291053cddb6b1bae2964282bbcd9cda.png


a7884635cf913033f7cd79e252717b00.png


代码演示

/**
 * 参数:-Xms10m -Xmx10m -XX:+PrintGCDetails
 * @author shkstart  shkstart@126.com
 * @create 2020  17:28
 */
public class SimpleHeap {
    private int id;//属性、成员变量
    public SimpleHeap(int id) {
        this.id = id;
    }
    public void show() {
            SimpleHeap sl = new SimpleHeap(1);
            SimpleHeap s2 = new SimpleHeap(2);
            int[] arr = new int[10];
            Object[] arr1 = new Object[10];
    }
    public static void main(String[] args){
        System.out.println();
    }
}

运行结果:

82362d35e5c477ce2119e169ce9e551c.jpg

eef030cb62eca0071b55e93de61089ff.jpg



相关文章
|
10月前
|
弹性计算 定位技术 数据中心
阿里云服务器配置选择方法:付费类型、地域及CPU内存配置全解析
阿里云服务器怎么选?2025最新指南:就近选择地域,降低延迟;长期使用选包年包月,短期灵活选按量付费;企业选2核4G5M仅199元/年,个人选2核2G3M低至99元/年,高性价比爆款推荐,轻松上云。
1105 11
|
存储 安全 iOS开发
内存卡怎么格式化?6个格式化方法供你选
随着使用时间的增加,内存卡可能会因为数据积累、兼容性或是文件系统损坏等原因需要进行格式化。那么怎样正确格式化内存卡呢?格式化内存卡的时候需要注意什么呢?本文会给大家提供详细的步骤,帮助大家轻松完成格式化内存卡的操作。
|
JavaScript 前端开发 Java
垃圾回收机制会导致内存泄漏吗?
【10月更文挑战第29天】虽然JavaScript的垃圾回收机制本身是为了有效地管理内存,但开发者在编写代码时需要注意上述这些可能导致内存泄漏的情况,遵循良好的编程习惯,及时释放不再使用的资源,以确保程序能够高效地利用内存资源,避免出现内存泄漏问题。
|
监控 JavaScript Java
Node.js中内存泄漏的检测方法
检测内存泄漏需要综合运用多种方法,并结合实际的应用场景和代码特点进行分析。及时发现和解决内存泄漏问题,可以提高应用的稳定性和性能,避免潜在的风险和故障。同时,不断学习和掌握内存管理的知识,也是有效预防内存泄漏的重要途径。
1134 159
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
存储 Windows
内存卡坏了还能修吗?4种常见修复方法
内存卡出现“无法保存”或“存储异常”等问题时,不一定是硬件损坏,可能是系统错误或文件系统异常导致。本文介绍几种亲测有效的修复方法:1) 更换读卡设备排除接触问题;2) 格式化修复文件系统(需先备份数据);3) 使用DiskGenius检测坏道;4) 借助厂商工具深度修复。同时提供日常保养建议,如避免高温环境、养成数据备份习惯,延长内存卡使用寿命。通过这些方法,多数问题可轻松解决,无需更换硬件。
|
存储 IDE Java
java设置栈内存大小
在Java应用中合理设置栈内存大小是确保程序稳定性和性能的重要措施。通过JVM参数 `-Xss`,可以灵活调整栈内存大小,以适应不同的应用场景。本文介绍了设置栈内存大小的方法、应用场景和注意事项,希望能帮助开发者更好地管理Java应用的内存资源。
973 4
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
1378 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
548 6
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
484 5