Java 基础数据类型占用内存空间和字符串编码简介(二)
创始人
2024-03-09 01:44:36
0

Java 基础数据类型占用内存空间简介

  • 一 计算机简介
    • 1.基本概念
    • 2.CPU 三级缓存
    • 3.本机参数查看
  • 二 数据占用内存情况
    • 1.多线程Demo
    • 2.结果解析
      • 1.直接计算
      • 2.volatile 计算
      • 3.缓存行填充

一 计算机简介

结合多线程计算机的硬件,从侧面理解数据存储如何影响我们的程序

1.基本概念

1.RAM:随机存储(主存等,断电数据丢失)
2.ROM:只读存储(磁盘等,断点数据保留)
3.BIOS:烧录在主板上ROM内的一段程序(基础输入/输出系统)1.线程:程序或应用的某个功能点,由CPU分配时间片,进行调度
2.进程:完整的运行中的程序或应用,由操作系统进行调度1.单核单线程:一个核心,只能运行一个线程,完全结束后才能开始下一个(串行)
2.单核多线程:一个核心,能调度多个线程,多个线程在某个时间段的不同时间片上运行(并发)
3.多核多线程:多个核心,每个核心至少支持一个线程,至少能有核心数的线程在某个时刻运行(并行)
4.DMA(Direct Memory Access):直接内存控制器,即CPU进行文件复制时,将总线控制权交给DMA,由DMA进行复制,此时CPU能继续进行内部运算或挂起,DMA结束后交还控制权;CPU 与 DMA 还可交替访问内存,此时总线相当于一个换路器。DMA也是我们常说的【零拷贝技术】依赖的硬件基础1.并发:一个计算单元(CPU核心),某一时刻只能运行一个线程,某个时间段多个线程在这个核心内轮询执行就是并发所以并发在本质上调度仍然是串行,只不过每个时间片很短,给我们的感觉是所有程序同时在运行其实,由此可以引出多线程开发设置核心线程数的参考原因:CPU密集型:核心线程数最好与实际核心数一致,CPU密集则尽量使每个线程落在一个核心上,使他们之间发挥并行的效果,核心线程数过大则会在某个核频繁切换上下文(轮询),切换上下文涉及现场保存和恢复等,会消耗性能IO密集型:核心线程数约为实际核心数二倍,由于IO操作的耗时性,当IO等待时,可以让CPU去执行其他线程操作(反正要等待,不如顺便切换几次上下文)
2.并行:多个计算单元(CPU核心),某一个时刻分别运行了一个线程,则这几个线程此刻是并行的

2.CPU 三级缓存

CPU从硬件角度来看,本身设计也是多级缓存的,结构示意如图,而多级缓存的主要目的,和

软件一样,为了提升程序执行和数据访问速度;但是既然是缓存,就涉及到数据一致的问题,

多核CPU下,常见的缓存一致性协议有 MESI

在这里插入图片描述

三级缓存下 ALU 访问资源时间

资源对象周期(可有频率换算)时间(纳秒)
寄存器1 cycle
L1 Cache~3-4 cycles~0.5-1 ns
L2 Cache~10-20 cycles~3-7 ns
L3 Cache~40-45 cycles~15 ns
内存~120-240 cycles~60-120ns

3.本机参数查看

其中逻辑处理器,是基于物理内核虚拟出来的,本机共 4 个内核,虚拟后相当于有 8 个内核

在这里插入图片描述

二 数据占用内存情况

在 Java 里面,使用 volatile 修饰变量时,可能会存在一个伪内存共享问题,我们下面演示一下

volatile 本身有两个作用:可见性、防止指令重排

程序运行时,我们的数据不是一位一位加载的,而是一块一块的,缓存的最小结构是缓存行

,一次填充一个缓存行的数据,这样做也是为了提高处理速度,缓存行大小一般为 64 字节;

volatile 如何保证可见性呢?
当某个线程更新本地缓存中的 value 值后,会使其他线程的本地缓存中的 value 值失效,然后其他线程需要重新去主存取数据,也就保证了可见性
但是由于 value 是存在缓存行内的,每次置失效都要清掉整行数据,重新获取,此时即存在性能损耗

1.多线程Demo

package org.example;import org.openjdk.jol.info.ClassLayout;/*** @author * @date 2022-11-08 22:44* @since 1.8*/
public class CpuCache {public static void main(String[] args) {DemoEntity demo = new DemoEntity();int length = 10000 * 10000;Thread t1 = new Thread(()->{for (int i = 0 ;i < length;i++){demo.a++;}});Thread t2 = new Thread(()->{for (int i = 0 ;i < length;i++){demo.f++;}});long start = System.currentTimeMillis();t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {throw new RuntimeException(e);}long end = System.currentTimeMillis() - start;System.out.println(ClassLayout.parseInstance(demo).toPrintable());System.out.println(String.format("Time:%s Data %s",end,demo));}/*** 测试对象*/static class DemoEntity{private long a;private long f;@Overridepublic String toString(){return String.format("a:%s b:%s",a,f);}}
}

2.结果解析

1.直接计算

线程 1 和 2 都加载了数据时,各自计算,12 毫秒完成了计算

在这里插入图片描述

2.volatile 计算

修改代码,在 DemoEntity 的两个变量前加上 volatile 关键字修饰,重新运行;耗时 2790 毫秒 ?
解析:
上面我们说,数据是加载到缓存行的,我们添加了 volatile 关键字后,两个线程修改同一个对象时,为了保证可见性,会将其他其他缓存行数据清空
线程 1 和 2 的数据大概率加载后会在同一个缓存行,他们互相清空和争抢资源,导致耗时

在这里插入图片描述

3.缓存行填充

原来一个 DemoEntity 对象 32 字节,我们在变量 a 和 f 之间填充两个 long 变量将 线程 2 的数据挤到下一个缓存行
耗时 556 毫秒 

在这里插入图片描述

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...