Java对象头详解

Java对象头详解

由于Java面向对象的思想,在JVM中需要大量存储对象,存储时为了实现一些额外的功能,需要在对象中添加一些标记字段用于增强对象功能,这些标记字段组成了对象头。 1.对象头形式 JVM中对象头的方式有以下两种(以32位JVM为例): 1.1.普通对象 |--------------------------------------------------------------| | Object Header (64 bits) | |------------------------------------|-------------------------| | Mark Word (32 bits) | Klass Word (32 bits) | |------------------------------------|-------------------------| |---------------------------------------------------------------------------------| | Object Header (96 bits) | |--------------------------------|-----------------------|------------------------| | Mark Word(32bits) | Klass Word(32bits) | array length(32bits) | |--------------------------------|-----------------------|------------------------| 2.对象头的组成 2.1.Mark Word 这部分主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等。mark word的位长度为JVM的一个Word大小,也就是说32位JVM的Mark word为32位,64位JVM为64位。 为了让一个字大小存储更多的信息,JVM将字的最低两个位设置为标记位,不同标记位下的Mark Word示意如下: |-------------------------------------------------------|--------------------| | Mark Word (32 bits) | State | |-------------------------------------------------------|--------------------| | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 | Normal | |-------------------------------------------------------|--------------------| | thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 | Biased | |-------------------------------------------------------|--------------------| | ptr_to_lock_record:30 | lock:2 | Lightweight Locked | |-------------------------------------------------------|--------------------| | ptr_to_heavyweight_

☕【JVM原理探索】分析堆外内存(Direct Memory)使用和分析

☕【JVM原理探索】分析堆外内存(Direct Memory)使用和分析

这是我参与更文挑战的第19天,活动详情查看: 更文挑战 堆外内存 堆外内存,其实就是不受JVM控制的内存。简单来说,除了堆栈内存,剩下的就都是堆外内存了(当然,这是从Java运行时内存的角度来看),堆外内存直接受操作系统管理,而不是虚拟机。而使用堆外内存的原因, 相比于堆内内存有几个优势: 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到) 堆外内存是直接受操作系统管理的,而不是JVM,因此使用堆外内存的话,就可以保持一个比较小的堆内内存,减少垃圾回收对程序性能的影响。 就是提高IO操作的效率!这里就涉及用户态与内核态,以及内核缓冲区的概念,如果从堆内向磁盘写数据,数据会被先复制到堆外内存,即内核缓冲区,然后再由OS写入磁盘,但使用堆外内存的话则可以避免这个复制操作。 堆内内存其实就是用户进程的【进程缓冲区】,属于用户态;堆外内存由操作系统管理【内核缓冲区】,属于内核态。 自然也有不好的一面: 堆外内存难以控制,如果内存泄漏,那么很难排查 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。 因为是操作系统的内存机制,所以需要通过本地方法进行分配,较为复杂和缓慢 直接内存使用 堆外内存通过java.nio的ByteBuffer来创建,调用allocateDirect方法申请即可。 可以通过设置-XX:MaxDirectMemorySize=10M控制堆外内存的大小。 堆外内存的垃圾回收 由于堆外内存并不直接控制于JVM,因此只能等到full GC的时候才能垃圾回收!Full GC,一般发生在年老代垃圾回收以及调用System.gc的时候,这样肯定不能满足我们的需求! 手动的控制回收堆外内存了!其中sun.nio其实是java.nio的内部实现。 package xing.test; import java.nio.ByteBuffer; import sun.nio.ch.DirectBuffer; public class NonHeapTest { public static void clean(final ByteBuffer byteBuffer) { if (byteBuffer.isDirect()) { ((DirectBuffer)byteBuffer).cleaner().clean(); } } public static void sleep(long i) { try { Thread.sleep(i); }catch(Exception e) { /*skip*/ } } public static void main(String []args) throws Exception { ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 200); System.out.println("start"); sleep(5000); c

利用开源 Sandboxie-Plus 为 Windows 桌面程序创建隔离沙箱环境 守护隐私 保持系统干净整洁

利用开源 Sandboxie-Plus 为 Windows 桌面程序创建隔离沙箱环境 守护隐私 保持系统干净整洁

Sandboxie-Plus 是什么 官网:https://sandboxie-plus.com/ GitHub:https://github.com/sandboxie-plus Sandboxie 是一款最早发布于 2004 年的老牌 Windows 沙盒软件,它可以创建一个隔离环境,在其中安装、运行程序而不影响沙盒外的文件。2020 年起,Sandboxie 转交开源社区维护。 Sandboxie-Plus 是开源社区基于 Sandboxie 开发的增强版本,拥有基于 Qt 的现代 UI 和开源后添加的所有新功能。Sandboxie-Plus 可以创建多个沙盒,可以为每个沙盒提供快照管理器、精细的文件系统控制、权限控制和网络访问控制。可以对沙盒内程序限制网络访问、限制资源访问(文件、注册表等)、限制提权、阻止访问剪贴板等。 Sandboxie-Plus 的第一个 1.0 稳定版发布于 2021 年 12 月 25 日,目前还在积极开发中。经笔者测试,目前的最新版 1.0.7 在 Windows 10 和 Windows 11 下都能正常使用,在 GitHub 上反馈的问题基本都能得到响应和修复。得益于热心的贡献者,UI 上的大部分元素都有中文翻译。 快速入门 Sandboxie-Plus (非便携模式) 默认将沙盘配置文件存放在 C:\Windows\Sandboxie.ini,备份此文件即可备份所有沙盘的配置。沙盘文件目录在 C:\Sandbox\[当前用户名]\[沙盘名] 中,在此路径下双击打开任何软件都将强制在沙盘中打开。沙盘中程序创建、修改的注册表位于 HKEY_USERS\Sandbox_[当前用户名]_[沙盘名] 中。当然,这些路径都可以在「设置-高级选项」中修改。 安装完成后还可以可选的在文件资源管理器右键菜单中添加「在沙盘中运行」项,方便使用。注意,这个文件必须要在你选择的沙盘中可读取,否则沙盘会报错。 在沙盘中运行程序与直接运行几乎没有区别,默认情况下在标题会有 [#] 标记,以及在鼠标移到标题时显示黄色边框,这些外观样式都可以在对应沙盘选项中修改。 ⚠️警告:不要认为隔离是100%的,即使虚拟机都存在被逃逸的可能,追求完美请使用物理方法隔离,不要尝试在沙盘中测试病毒! 沙盒模板简单介绍 部分功能需要捐赠,详见下方「捐赠获得支持者凭证(Supporter Certificate)」 Sandboxie-Plus 默认的 DefaultBox 即为标准隔离沙盒。这个沙盒模板默认不限制对所有文件的读取,但沙盒内程序无法写入、修改、删除沙盒外的文件、注册表。在沙盒内创建的文件会被重定向到该沙盒目录中,修改文件会在沙盒目录中创建一个副本进行修改,删除文件会在沙盒目录中建立一个对应的0字节文件。 安全防护加固型沙盒是在标准隔离沙盒的基础上,加上勾选「常

count(1)、count(*)与count(列名)的执行区别

count(1)、count(*)与count(列名)的执行区别

执行效果: 1. count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count()用时多了!从执行计划来看,count(1)和count()的效果是一样的。但是在表做过分析之后,count(1)会比count(*)的用时少些(1w以内数据量),不过差不了多少。 如果count(1)是聚索引,id,那肯定是count(1)快。但是差的很小的。 因为count(),自动会优化指定到那一个字段。所以没必要去count(1),用count(),sql会帮你完成优化的 因此:count(1)和count(*)基本没有差别! 2. count(1) and count(字段) 两者的主要区别是 count(1) 会统计表中的所有的记录数,包含字段为null 的记录。 count(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录。 出自:http://www.cnblogs.com/Dhouse/p/6734837.html count(*) 和 count(1)和count(列名)区别 执行效果上: count(*)包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL count(1)包括了忽略所有列,用1代表代码行,在统计结果的时候,不会忽略列值为NULL count(列名)只包括列名那一列,在统计结果的时候,会忽略列值为空(这里的空不是只空字符串或者0,而是表示null)的计数,即某个字段值为NULL时,不统计。 执行效率上: 列名为主键,count(列名)会比count(1)快 列名不为主键,count(1)会比count(列名)快 如果表多个列并且没有主键,则 count(1) 的执行效率优于 count(*) 如果有主键,则 select count(主键)的执行效率是最优的 如果表只有一个字段,则 select count(*)最优。 出自:http://eeeewwwqq.iteye.com/blog/1972576 实例分析 mysql> create table counttest(name char(1), age char(2)); Query OK, 0 rows affected (0.03 sec) mysql> insert into counttest values     -> ('a', '14'),('a', '15'), ('a', '15'),     -> ('b', NULL), ('b', '16'),     -> ('c', '17'),     -> ('d', null),     ->('e', ''); Query OK, 8 rows affected (0.01 sec) Records: 8  Duplicates: 0  Warnings: 0 mysql> select * from counttest; +------+------+ | name | age  | +------+------+ | a    | 14   | | a    | 15   | | a    | 15   | | b    | NULL | | b    | 16   | | c    | 17   | | d    | NULL | | e    |      | +------+------+ 8 rows in set (0.00 sec) mysql> select name, count(name), count(1), count(*), count(ag

Java并发:volatile内存可见性和指令重排

Java并发:volatile内存可见性和指令重排

volatile两大作用 1、保证内存可见性 2、防止指令重排 此外需注意volatile并不保证操作的原子性。 (一)内存可见性 1 概念 JVM内存模型:主内存和线程独立的工作内存 Java内存模型规定,对于多个线程共享的变量,存储在主内存当中,每个线程都有自己独立的工作内存(比如CPU的寄存器),线程只能访问自己的工作内存,不可以访问其它线程的工作内存。 工作内存中保存了主内存共享变量的副本,线程要操作这些共享变量,只能通过操作工作内存中的副本来实现,操作完毕之后再同步回到主内存当中。 如何保证多个线程操作主内存的数据完整性是一个难题,Java内存模型也规定了工作内存与主内存之间交互的协议,定义了8种原子操作: (1) lock:将主内存中的变量锁定,为一个线程所独占 (2) unclock:将lock加的锁定解除,此时其它的线程可以有机会访问此变量 (3) read:将主内存中的变量值读到工作内存当中 (4) load:将read读取的值保存到工作内存中的变量副本中。 (5) use:将值传递给线程的代码执行引擎 (6) assign:将执行引擎处理返回的值重新赋值给变量副本 (7) store:将变量副本的值存储到主内存中。 (8) write:将store存储的值写入到主内存的共享变量当中。 通过上面Java内存模型的概述,我们会注意到这么一个问题,每个线程在获取锁之后会在自己的工作内存来操作共享变量,操作完成之后将工作内存中的副本回写到主内存,并且在其它线程从主内存将变量同步回自己的工作内存之前,共享变量的改变对其是不可见的。即其他线程的本地内存中的变量已经是过时的,并不是更新后的值。 2 内存可见性带来的问题 很多时候我们需要一个线程对共享变量的改动,其它线程也需要立即得知这个改动该怎么办呢?下面举两个例子说明内存可见性的重要性: 例子1 有一个全局的状态变量open: boolean open=true; 这个变量用来描述对一个资源的打开关闭状态,true表示打开,false表示关闭,假设有一个线程A,在执行一些操作后将open修改为false: //线程A resource.close(); open = false; 线程B随时关注open的状态,当open为true的时候通过访问资源来进行一些操作: //线程B while(open) { doSomethingWithResource(resource); } 当A把资源关闭的时候,open变量对线程B是不可见的,如果此时open变量的改动尚未同步到线程B的工作内存中,那么线程B就会用一个已经关闭了的资源去做一些操作,因此产生错误。 例子2 下面是一个通过布尔标志判断线程是否结束的例子: public class CancelTh

联系我们

联系电话

4000-640-466

联系邮箱

service@f-li.cn

办公地址

上海黄浦区外滩源1号

谢谢,您的信息已成功发送。
请填写信息。