精华内容
下载资源
问答
  • 把一个aspx网页进行了缓存后,当1000个访问者同时访问时,服务器的内存使用是多少? 如果是1,000,000个访问者同时访问呢?  就是说,aspx页面缓存后,内存消耗会因为访问者的数量增加而增加吗?明白我说什么吗? ...
    问:
    aspx页面缓存后, 内存消耗会因为访问者的数量增加而增加吗?
    把一个aspx网页进行了缓存后, 当有1000个访问者同时访问时, 服务器的内存使用是多少
    ?
    如果是1,
    000,000个访问者同时访问呢? 

    就是说, aspx页面缓存后, 内存消耗会因为访问者的数量增加而增加吗? 明白我说什么吗?

    答:

    asp.net页面缓存可以根据不同条件对同一页面保留不同缓存页。

    比如同样对于A.aspx页面,假设设置为根据URL上的参数X的不同而缓存不同版本页面,则当X
    =1时系统将保留1份缓存,当X=2时,系统再保留一份缓存,这样系统中对于A页面有2个缓存,占用2份内存,但是对于所有访问X=1和X=2的A页面的所有用户请求,不管是 1,000个还是100,000,000个,都只是分别访问内存中的这2份缓存中的其中一个而已,不会再增加额外的内存消耗,除非有一个人请求X=3的页面,当有人申请X=3的页面时,系统会再增加一个针对X=3的缓存,从而系统中保留了3份A页面缓存。

    页面缓存只会根据设置的条件的不同而缓存多份页面,而不是根据请求的数量。对于条件的多个值会缓存多份页面,但是对于同一个条件的请求,总是使用同一份缓存。

     背景介绍:

        为了帮助您提高应用程序的性能,ASP.NET 使用两种基本的缓存机制来提供缓存功能。第一种机制是应用程序缓存,它允许您缓存所生成的数据,如 DataSet 或自定义报表业务对象。第二种机制是页输出缓存,它保存页处理输出,并在用户再次请求该页时,重用所保存的输出,而不是再次处理该页。
         出于以下原因之一,ASP.NET 可以从缓存中移除数据:
    1.由于服务器上的内存不足,开始一个称为“清理”的过程。
    2.由于缓存中的项已过期。
    3.由于项的依赖项发生了更改。 
         为了帮助管理缓存项,在将项从缓存中移除时,ASP.NET 会通知应用程序。

     

    来源:

     微软中文技术论坛(MSDN and TechNet) » Web 技术 » ASP.NET 与 AJAX » aspx页面缓存后, 内存消耗会因为访问者的数量增加而增加吗?

     

    转载于:https://www.cnblogs.com/badapple126/archive/2008/10/22/1316435.html

    展开全文
  • 一、什么缓存缓存就是数据交换的缓冲区(称作:Cache),他把一些外存上的数据保存在内存上,为什么保存在内存上,我们运行的所有程序里面的变量都是存放在内存中的,所以如果想将值放入内存上,可以通过变量的...

    56b72721bd9ed2f63e2f40929b7d16a3.png

    一、什么是缓存

    缓存就是数据交换的缓冲区(称作:Cache),他把一些外存上的数据保存在内存上,为什么保存在内存上,我们运行的所有程序里面的变量都是存放在内存中的,所以如果想将值放入内存上,可以通过变量的方式存储。在JAVA中一些缓存一般都是通过Map集合来实现的。

    缓存在不同的场景下,作用是不一样的具体举例说明: ✔ 操作系统磁盘缓存 ——> 减少磁盘机械操作。 ✔ 数据库缓存——>减少文件系统IO。 ✔ 应用程序缓存——>减少对数据库的查询。 ✔ Web服务器缓存——>减少应用服务器请求。 ✔ 客户端浏览器缓存——>减少对网站的访问。

    具体关于缓存的详细介绍以及缓存的面试问题可以参考这篇博客

    https://blog.csdn.net/zhengzhaoyang122/article/details/82184029

    二、Spring缓存抽象

    Spring从3.1开始定义了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;

    每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。

    使用Spring缓存抽象时我们需要关注以下两点;

    1、确定方法需要被缓存以及他们的缓存策略

    2、从缓存中读取之前缓存存储的数据

    ee14ae8eb46fcc76be6740e465df8cdc.png

    三、几个重要概念&缓存注解

    1、Cache:

    缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等

    2、CacheManager

    缓存管理器,管理各种缓存(Cache)组件

    3、@Cacheable

    主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

    (后面会细说该注解)

    4、@CacheEvict

    清空缓存

    5、@CachePut

    保证方法被调用,又希望结果被缓存。

    6、@EnableCaching

    开启基于注解的缓存

    7、keyGenerator

    缓存数据时key生成策略

    8、serialize

    缓存数据时value序列化策略

    9、@Cacheable/@CachePut/@CacheEvict 主要的参数

    8f758a55dd6482f1887aed0a709685f3.png

    f67e32957fdd2f5217b1afdf81b3c739.png

    四、springboot整合redis实现缓存

    1、引入依赖

    在pom.xml中引入spring-boot-starter-data-redis依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    2、配置redis连接地址

    在application.yml或者application.properties中配置redis连接地址

    这里还需要配置一下数据库的地址,方便测试使用

    application.properties配置

    spring.datasource.url=jdbc:mysql://MySQL的主机地址:3306/数据库名
    spring.datasource.username=root
    spring.datasource.password=密码
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    
    # 启用mybatis的命名策略(即驼峰命名法)
    mybatis.configuration.map-underscore-to-camel-case=true
    
    logging.level.com.atguigu.cache.mapper=debug
    
    debug=true
    
    spring.redis.host=redis主机地址

    application.yml配置:

    spring:
      datasource:
        username: root
        password: 123456
        url: jdbc:mysql://MySQL的主机地址:3306/数据库名
        driver-class-name: com.mysql.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource #配置Druid数据源
    
        #   数据源其他配置
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
      redis:
        host: redis主机地址
    #mybatis:
    #  config-location: classpath:mybatis/mybatis-config.xml
    # 开启驼峰命名
    mybatis:
      configuration:
        map-underscore-to-camel-case: true
    logging:
      level:
        com.canghe.springboot.mapper: debug
    debug: true
    #  mapper-locations: classpath:mybatis/mapper/*.xml

    3、在虚拟机或者云服务器上安装redis

    可以直接通过docker安装redis,并配置端口

    4、使用RestTemplate操作redis

    @Autowired
        StringRedisTemplate stringRedisTemplate;//操作k-v都是字符串
    
        @Autowired
        RedisTemplate redisTemplate;//操作k-v对象的

    Redis常见的五大数据类型

    String(字符串) 、list(列表)、set(集合)、hash(散列)、Zset(有序集合)

    1. redisTemplate.opsForValue();//操作字符串
    2. redisTemplate.opsForHash();//操作hash
    3. redisTemplate.opsForList();//操作list
    4. redisTemplate.opsForSet();//操作set
    5. redisTemplate.opsForZSet();//操作有序set

    测试代码:

    @Test
        public void test01() {
    //        stringRedisTemplate.opsForValue().append("key","helloword");
    //        String msg = stringRedisTemplate.opsForValue().get("key");
    //        System.out.println("msg:"+msg);
            stringRedisTemplate.opsForList().leftPush("firstList","1");
            stringRedisTemplate.opsForList().leftPush("firstList","2");
        }

    五、@Cacheable注解

    1、运行流程

    1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;(CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数; key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key; SimpleKeyGenerator生成key的默认策略; 如果没有参数;key=new SimpleKey(); 如果有一个参数:key=参数的值 如果有多个参数:key=new SimpleKey(params);

    3、没有查到缓存就调用目标方法;

    4、将目标方法返回的结果,放进缓存中

    @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存, 如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;

    2、核心

    1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator

    3、几个属性

    • cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
    • key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值, 编写SpEL; #i d;参数id的值 #a0 #p0 #root.args[0] getEmp[2]
    • keyGenerator:key的生成器;可以自己指定key的生成器的组件id,key/keyGenerator:二选一使用;
    • cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
    • condition:指定符合条件的情况下才缓存; condition = "#a0>1":第一个参数的值》1的时候才进行缓存
    • unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断

    unless = "#result == null" * unless = "#a0==2":如果第一个参数的值是2,结果不缓存;

    • sync:是否使用异步模式

    @Cacheable例子:

    @Cacheable(value = {"emp"}/*,keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)
        public Employee getEmp(Integer id){
            System.out.println("查询"+id+"号员工");
            Employee emp = employeeMapper.getEmpById(id);
            return emp;
        }

    六、@CachePut

    1、启用场景

    既调用方法,又更新缓存数据;同步更新缓存,修改了数据库的某个数据,同时更新缓存;

    2、运行时机

    • 先调用目标方法
    • 将目标方法的结果缓存起来

    3、测试步骤

    1、查询1号员工;查到的结果会放在缓存中; key:1 value:lastName:张三 2、以后查询还是之前的结果 3、更新1号员工;【lastName:zhangsan;gender:0】 将方法的返回值也放进缓存了; key:传入的employee对象 值:返回的employee对象; 4、查询1号员工? 应该是更新后的员工; key = "#employee.id":使用传入的参数的员工id; key = "#result.id":使用返回后的id @Cacheable的key是不能用#result 为什么是没更新前的?【1号员工没有在缓存中更新】

    @CachePut(/*value = "emp",*/key = "#result.id")
        public Employee updateEmp(Employee employee){
            System.out.println("updateEmp:"+employee);
            employeeMapper.updateEmp(employee);
            return employee;
        }

    七、@CacheEvict

    缓存清除

    /**
         * @CacheEvict:缓存清除
         *  key:指定要清除的数据
         *  allEntries = true:指定清除这个缓存中所有的数据
         *  beforeInvocation = false:缓存的清除是否在方法之前执行
         *      默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
         *
         *  beforeInvocation = true:
         *      代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
         *
         *
         */
        @CacheEvict(value="emp",beforeInvocation = true/*key = "#id",*/)
        public void deleteEmp(Integer id){
            System.out.println("deleteEmp:"+id);
            //employeeMapper.deleteEmpById(id);
            int i = 10/0;
        }

    八、@Caching

    定义复杂的缓存规则

    @Caching(
             cacheable = {
                 @Cacheable(/*value="emp",*/key = "#lastName")
             },
             put = {
                 @CachePut(/*value="emp",*/key = "#result.id"),
                 @CachePut(/*value="emp",*/key = "#result.email")
             }
        )

    The more effort,the more lucky

    GitHub项目

    https://github.com/freestylefly/javaStudy

    b5369a1c435d001d2717d43be0b6aa82.png

    82dacdb9ee1c91837ccde0acff5a129c.png

    我的CSDN博客

    https://blog.csdn.net/qq_43270074

    efb950ea08c631743cb17e40a3c113c9.png

    个人博客

    http://canghe.top/

    12a507c20286a57beb718b326f4f6a25.png

    微信公众号

    苍何之旅

    展开全文
  • Linux的缓存内存(cache memory)

    万次阅读 2018-01-27 14:55:37
    其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory。 为什么Linux系统没运行多少程序,显示的可用内存这么少?其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,...

    PS:为什么Linux系统没运行多少程序,显示的可用内存这么少?其实Linux与Win的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory。

    为什么Linux系统没运行多少程序,显示的可用内存这么少?其实Linux与Win的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory。

    有时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存free项会很少,此时查看系统的 /proc/meminfo 文件,会发现有一项 Cached Memory:
    输入cat /proc/meminfo查看:

    MemTotal: 16425996 kB
        MemFree: 5698808 kB
        Buffers: 380904 kB
        Cached: 9389356 kB
        SwapCached: 212 kB
        Active: 6569200 kB
        Inactive: 3725364 kB
        HighTotal: 0 kB
        HighFree: 0 kB
        LowTotal: 16425996 kB
        LowFree: 5698808 kB
        SwapTotal: 8273464 kB
        SwapFree: 8273252 kB
        Dirty: 980 kB
        Writeback: 0 kB
        AnonPages: 524108 kB
        Mapped: 24568 kB
        Slab: 381776 kB
        PageTables: 7496 kB
        NFS_Unstable: 0 kB
        Bounce: 0 kB
        CommitLimit: 16486460 kB
        Committed_AS: 2143856 kB
        VmallocTotal: 34359738367 kB
        VmallocUsed: 267656 kB
        VmallocChunk: 34359469303 kB
        HugePages_Total: 0
        HugePages_Free: 0
        HugePages_Rsvd: 0
        Hugepagesize: 2048 kB
    

    free命令里各项内存指标说明:

    total used free shared buffers cached
    Mem: 16425996 10727220 5698776 0 380904 9389832
    -/+ buffers/cache: 956484 15469512
    Swap: 8273464 212 8273252
    

    其中第一行用全局角度描述系统使用的内存状况:
    total——总物理内存
    used——已使用内存,一般情况这个值会比较大,因为这个值包括了cache+应用程序使用的内存
    free——完全未被使用的内存
    shared——应用程序共享内存
    buffers——缓存,主要用于目录方面,inode值等(ls大目录可看到这个值增加)
    cached——缓存,用于已打开的文件
    总结:
    total=used+free
    used=buffers+cached (maybe add shared also)

    第二行描述应用程序的内存使用:
    前个值表示-buffers/cache——应用程序使用的内存大小,used减去缓存值
    后个值表示+buffers/cache——所有可供应用程序使用的内存大小,free加上缓存值
    总结:
    -buffers/cache=used-buffers-cached
    +buffers/cache=free+buffers+cached

    第三行表示swap的使用:
    used——已使用
    free——未使用

    什么是Cache Memory(缓存内存):

    当你读写文件的时候,Linux内核为了提高读写性能与速度,会将文件在内存中进行缓存,这部分内存就是Cache Memory(缓存内存)。即使你的程序运行结束后,Cache Memory也不会自动释放。这就会导致你在Linux系统中程序频繁读写文件后,你会发现可用物理内存会很少。

    其实这缓存内存(Cache Memory)在你需要使用内存的时候会自动释放,所以你不必担心没有内存可用。如果你希望手动去释放Cache Memory也是有办法的。

    如何释放Cache Memory(缓存内存):

    用下面的命令可以释放Cache Memory:

    To free pagecache:
    echo 1 > /proc/sys/vm/drop_caches
    To free dentries and inodes:
    echo 2 > /proc/sys/vm/drop_caches
    To free pagecache, dentries and inodes:
    echo 3 > /proc/sys/vm/drop_caches
    

    注意,释放前最好sync一下,防止丢失数据。

    总结:个人经验认为没必要手动释放,这种内存管理方式也是比win优胜的地方之一!因为Linux的内核内存管理机制,一般情况下不需要特意去释放已经使用的cache。这些cache起来的内容可以提高文件以及磁盘的读写速度。

    页面缓存——内存与文件的那些事儿

    提到文件,操作系统必须解决两个重要的问题。首先是硬盘驱动器的存取速度缓慢得令人头疼(相对于内存而言),尤其是磁盘的寻道性能。第二个是要满足‘一次性加载文件内容到物理内存并在程序间共享’的需求。如果你使用进程浏览器翻看Windows进程,就会发现大约15MB的共享DLL被加载进了每一个进程。我目前的Windows系统就运行了100个进程,如果没有共享机制,那将消耗大约1.5GB的物理内存仅仅用于存放公用DLL。这可不怎么好。同样的,几乎所有的Linux程序都需要ld.so和libc,以及其它的公用函数库。

    令人愉快的是,这两个问题可以被一石二鸟的解决:页面缓存(page cache),内核用它保存与页面同等大小的文件数据块。为了展示页面缓存,我需要祭出一个名叫render的Linux程序,它会打开一个scene.dat文件,每次读取其中的512字节,并将这些内容保存到一个建立在堆上的内存块中。首次的读取是这样的:
    这里写图片描述
    在读取了12KB以后,render的堆以及相关的页帧情况如下:
    这里写图片描述
    这看起来很简单,但还有很多事情会发生。首先,即使这个程序只调用了常规的read函数,此时也会有三个 4KB的页帧存储在页面缓存当中,它们持有scene.dat的一部分数据。尽管有时这令人惊讶,但的确所有的常规文件I/O都是通过页面缓存来进行的。在x86 Linux里,内核将文件看作是4KB大小的数据块的序列。即使你只从文件读取一个字节,包含此字节的整个4KB数据块都会被读取,并放入到页面缓存当中。这样做是有道理的,因为磁盘的持续性数据吞吐量很不错,而且一般说来,程序对于文件中某区域的读取都不止几个字节。页面缓存知道每一个4KB数据块在文件中的对应位置,如上图所示的#0, #1等等。与Linux的页面缓存类似,Windows使用256KB的views。

    不幸的是,在一个普通的文件读取操作中,内核必须复制页面缓存的内容到一个用户缓冲区中,这不仅消耗CPU时间,伤害了CPU cache的性能,还因为存储了重复信息而浪费物理内存。如上面每张图所示,scene.dat的内容被保存了两遍,而且程序的每个实例都会保存一份。至此,我们缓和了磁盘延迟的问题,但却在其余的每个问题上惨败。内存映射文件(memory-mapped files)将引领我们走出混乱:
    这里写图片描述
    当你使用文件映射的时候,内核将你的程序的虚拟内存页直接映射到页面缓存上。这将导致一个显著的性能提升:《Windows系统编程》指出常规的文件读取操作运行时性能改善30%以上;《Unix环境高级编程》指出类似的情况也发生在Linux和Solaris系统上。你还可能因此而节省下大量的物理内存,这依赖于你的程序的具体情况。

    和以前一样,提到性能,实际测量才是王道,但是内存映射的确值得被程序员们放入工具箱。相关的API也很漂亮,它提供了像访问内存中的字节一样的方式来访问一个文件,不需要你多操心,也不牺牲代码的可读性。回忆一下地址空间、还有那个在Unix类系统上关于mmap的实验,Windows下的CreateFileMapping及其在高级语言中的各种可用封装。当你映射一个文件时,它的内容并不是立刻就被全部放入内存的,而是依赖页故障(page fault)按需读取。在获取了一个包含所需的文件数据的页帧后,对应的故障处理函数会将你的虚拟内存页映射到页面缓存上。如果所需内容不在缓存当中,此过程还将包含磁盘I/O操作。

    现在给你出一个流行的测试题。想象一下,在最后一个render程序的实例退出之时,那些保存了scene.dat的页面缓存会被立刻清理吗?人们通常会这样认为,但这是个坏主意。如果你仔细想想,我们经常会在一个程序中创建一个文件,退出,紧接着在第二个程序中使用这个文件。页面缓存必须能处理此类情况。如果你再多想想,内核何必总是要舍弃页面缓存中的内容呢?记住,磁盘比RAM慢5个数量级,因此一个页面缓存的命中(hit)就意味着巨大的胜利。只要还有足够的空闲物理内存,缓存就应该尽可能保持满状态。所以它与特定的进程并不相关,而是一个系统级的资源。如果你一周前运行过render,而此时scene.dat还在缓存当中,那真令人高兴。这就是为什么内核缓存的大小会稳步增加,直到缓存上限。这并非因为操作系统是破烂货,吞噬你的RAM,事实上这是种好的行为,反而释放物理内存才是一种浪费。缓存要利用得越充分越好。

    由于使用了页面缓存体系结构,当一个程序调用write()时,相关的字节被简单的复制到页面缓存中,并且将页面标记为脏的(dirty)。磁盘I/O一般不会立刻发生,因此你的程序的执行不会被打断去等待磁盘设备。这样做的缺点是,如果此时计算机死机,那么你写入的数据将不会被记录下来。因此重要的文件,比如数据库事务记录必须被fsync() (但是还要小心磁盘控制器的缓存)。另一方面,读取操作一般会打断你的程序直到准备好所需的数据。内核通常采用积极加载(eager loading)的方式来缓解这个问题。以提前读取(read ahead)为例,内核会预先加载一些页到页面缓存,并期待你的读取操作。通过提示系统即将对文件进行的是顺序还是随机读取操作(参看madvise(), readahead(), Windows缓存提示),你可以帮助内核调整它的积极加载行为。Linux的确会对内存映射文件进行预取,但我不太确定Windows是否也如此。最后需要一提的是,你还可以通过在Linux中使用O_DIRECT或在Windows中使用NO_BUFFERING来绕过页面缓存,有些数据库软件就是这么做的。

    一个文件映射可以是私有的(private)或共享的(shared)。这里的区别只有在更改(update)内存中的内容时才会显现出来:在私有映射中,更改并不会被提交到磁盘或对其他进程可见,而这在共享的映射中就会发生。内核使用写时拷贝(copy on write)技术,通过页表项(page table entries),实现私有映射。在下面的例子中,render和另一个叫render3d的程序(我是不是很有创意?)同时私有映射了scene.dat。随后render改写了映射到此文件的虚拟内存区域:
    这里写图片描述
    上图所示的只读的页表项并不意味着映射是只读的,它们只是内核耍的小把戏,用于共享物理内存直到可能的最后一刻。你会发现‘私有’一词是多么的不恰当,你只需记住它只在数据发生更改时起作用。此设计所带来的一个结果就是,一个以私有方式映射文件的虚拟内存页可以观察到其他进程对此文件的改动,只要之前对这个内存页进行的都是读取操作。一旦发生过写时拷贝,就不会再观察到其他进程对此文件的改动了。此行为不是内核提供的,而是在x86系统上就会如此。另外,从API的角度来说,这也是合理的。与此相反,共享映射只是简单的映射到页面缓存,仅此而已。对页面的所有更改操作对其他进程都可见,而且最终会执行磁盘操作。最后,如果此共享映射是只读的,那么页故障将触发段错误(segmentation fault)而不是写时拷贝。

    被动态加载的函数库通过文件映射机制放入到你的程序的地址空间中。这里没有任何特别之处,同样是采用私有文件映射,跟提供给你调用的常规API别无二致。下面的例子展示了两个运行中的render程序的一部分地址空间,还有物理内存。它将我们之前看到的概念都联系在了一起。

    这里写图片描述

    展开全文
  • 什么要实现内存模型? •内存模型的就是为了在现代计算机平台中保证程序可以正确性的执行,但是不同的平台实现是不同的。 • 编译器中生成的指令顺序, 可以源代码中的顺序不同; • 编译器可能把变量保存在...
    为什么要实现内存模型?

    阅读:https://my.oschina.net/u/3728792/blog/3050325
    •内存模型的就是为了在现代计算机平台中保证程序可以正确性的执行,但是不同的平台实现是不同的。
    • 编译器中生成的指令顺序, 可以与源代码中的顺序不同;
    • 编译器可能把变量保存在寄存器而不是内存中;
    • 处理器可以采用乱序或并行等方式来执行指令;
    • 缓存可能会改变将写入变量提交到主内存的次序;
    • 保存在处理器本地缓存中的值,对其他处理器是不可见的;



    全面理解Java内存模型(JMM)及volatile关键字:
    https://blog.csdn.net/qq_32534441/article/details/88386473

     

    转自:https://blog.csdn.net/yaoyuanbo/article/details/81199154

    为什么要有内存模型


    在介绍Java内存模型之前,先来看一下到底什么是计算机内存模型,然后再来看Java内存模型在计算机内存模型的基础上做了哪些事情。要说计算机的内存模型,就要说一下一段古老的历史,看一下为什么要有内存模型。

    内存模型,英文名Memory Model,他是一个很老的老古董了。他是与计算机硬件有关的一个概念。那么我先给你介绍下他和硬件到底有啥关系。

    CPU和缓存一致性

    我们应该都知道,计算机在执行程序的时候,每条指令都是在CPU中执行的,而执行的时候,又免不了要和数据打交道。而计算机上面的数据,是存放在主存当中的,也就是计算机的物理内存啦。

    刚开始,还相安无事的,但是随着CPU技术的发展,CPU的执行速度越来越快。而由于内存的技术并没有太大的变化,所以从内存中读取和写入数据的过程和CPU的执行速度比起来差距就会越来越大,这就导致CPU每次操作内存都要耗费很多等待时间。

    这就像一家创业公司,刚开始,创始人和员工之间工作关系其乐融融,但是随着创始人的能力和野心越来越大,逐渐和员工之间出现了差距,普通员工原来越跟不上CEO的脚步。老板的每一个命令,传到到基层员工之后,由于基层员工的理解能力、执行能力的欠缺,就会耗费很多时间。这也就无形中拖慢了整家公司的工作效率。

    可是,不能因为内存的读写速度慢,就不发展CPU技术了吧,总不能让内存成为计算机处理的瓶颈吧。

    所以,人们想出来了一个好的办法,就是在CPU和内存之间增加高速缓存。缓存的概念大家都知道,就是保存一份数据拷贝。他的特点是速度快,内存小,并且昂贵。

    那么,程序的执行过程就变成了:

    当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。

    之后,这家公司开始设立中层管理人员,管理人员直接归CEO领导,领导有什么指示,直接告诉管理人员,然后就可以去做自己的事情了。管理人员负责去协调底层员工的工作。因为管理人员是了解手下的人员以及自己负责的事情的。所以,大多数时候,公司的各种决策,通知等,CEO只要和管理人员之间沟通就够了。

    而随着CPU能力的不断提升,一层缓存就慢慢的无法满足要求了,就逐渐的衍生出多级缓存。

    按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存(L1),二级缓存(L3),部分高端CPU还具有三级缓存(L3),每一级缓存中所储存的全部数据都是下一级缓存的一部分。

    这三种缓存的技术难度和制造成本是相对递减的,所以其容量也是相对递增的。

    那么,在有了多级缓存之后,程序的执行就变成了:

    当CPU要读取一个数据时,首先从一级缓存中查找,如果没有找到再从二级缓存中查找,如果还是没有就从三级缓存或内存中查找。

    随着公司越来越大,老板要管的事情越来越多,公司的管理部门开始改革,开始出现高层,中层,底层等管理者。一级一级之间逐层管理。

    单核CPU只含有一套L1,L2,L3缓存;

    如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。

    公司也分很多种,有些公司只有一个大Boss,他一个人说了算。但是有些公司有比如联席总经理、合伙人等机制。

    单核CPU就像一家公司只有一个老板,所有命令都来自于他,那么就只需要一套管理班底就够了。

    多核CPU就像一家公司是由多个合伙人共同创办的,那么,就需要给每个合伙人都设立一套供自己直接领导的高层管理人员,多个合伙人共享使用的是公司的底层员工。

    还有的公司,不断壮大,开始差分出各个子公司。各个子公司就是多个CPU了,互相之前没有共用的资源。互不影响。

    下图为一个单CPU双核的缓存结构。

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000246?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

     

     

    随着计算机能力不断提升,开始支持多线程。那么问题就来了。我们分别来分析下单线程、多线程在单核CPU、多核CPU中的影响。

    单线程。cpu核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题。

    单核CPU,多线程。进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。

    多核CPU,多线程。每个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,则每个核心都会在各自的caehe中保留一份共享内存的缓冲。由于多核是可以并行的,可能会出现多个线程同时写各自的缓存的情况,而各自的cache之间的数据就有可能不同。

    在CPU和主存之间增加缓存,在多线程场景下就可能存在缓存一致性问题,也就是说,在多核CPU中,每个核的自己的缓存中,关于同一个数据的缓存内容可能不一致。

    如果这家公司的命令都是串行下发的话,那么就没有任何问题。

    如果这家公司的命令都是并行下发的话,并且这些命令都是由同一个CEO下发的,这种机制是也没有什么问题。因为他的命令执行者只有一套管理体系。

    如果这家公司的命令都是并行下发的话,并且这些命令是由多个合伙人下发的,这就有问题了。因为每个合伙人只会把命令下达给自己直属的管理人员,而多个管理人员管理的底层员工可能是公用的。

    比如,合伙人1要辞退员工a,合伙人2要给员工a升职,升职后的话他再被辞退需要多个合伙人开会决议。两个合伙人分别把命令下发给了自己的管理人员。合伙人1命令下达后,管理人员a在辞退了员工后,他就知道这个员工被开除了。而合伙人2的管理人员2这时候在没得到消息之前,还认为员工a是在职的,他就欣然的接收了合伙人给他的升职a的命令。

     

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000484?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    处理器优化和指令重排

    上面提到在在CPU和主存之间增加缓存,在多线程场景下会存在缓存一致性问题。除了这种情况,还有一种硬件问题也比较重要。那就是为了使处理器内部的运算单元能够尽量的被充分利用,处理器可能会对输入代码进行乱序执行处理。这就是处理器优化

    除了现在很多流行的处理器会对代码进行优化乱序处理,很多编程语言的编译器也会有类似的优化,比如Java虚拟机的即时编译器(JIT)也会做指令重排

    可想而知,如果任由处理器优化和编译器对指令重排的话,就可能导致各种各样的问题。

    关于员工组织调整的情况,如果允许人事部在接到多个命令后进行随意拆分乱序执行或者重排的话,那么对于这个员工以及这家公司的影响是非常大的。

     

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725111959567?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    并发编程的问题

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725111959554?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    前面说的和硬件有关的概念你可能听得有点蒙,还不知道他到底和软件有啥关系。但是关于并发编程的问题你应该有所了解,比如原子性问题,可见性问题和有序性问题。

    其实,原子性问题,可见性问题和有序性问题。是人们抽象定义出来的。而这个抽象的底层问题就是前面提到的缓存一致性问题、处理器优化问题和指令重排问题等。

    这里简单回顾下这三个问题,并不准备深入展开,感兴趣的读者可以自行学习。我们说,并发编程,为了保证数据的安全,需要满足以下三个特性:

    原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。

    可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

    有序性即程序执行的顺序按照代码的先后顺序执行。

    有没有发现,缓存一致性问题其实就是可见性问题。而处理器优化是可以导致原子性问题的。指令重排即会导致有序性问题。所以,后文将不再提起硬件层面的那些概念,而是直接使用大家熟悉的原子性、可见性和有序性。

     

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725111959580?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    什么是内存模型

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725111959594?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    前面提到的,缓存一致性问题、处理器器优化的指令重排问题是硬件的不断升级导致的。那么,有没有什么机制可以很好的解决上面的这些问题呢?

     

    最简单直接的做法就是废除处理器和处理器的优化技术、废除CPU缓存,让CPU直接和主存交互。但是,这么做虽然可以保证多线程下的并发问题。但是,这就有点因噎废食了。

    所以,为了保证并发编程中可以满足原子性、可见性及有序性。有一个重要的概念,那就是——内存模型。

    为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。通过这些规则来规范对内存的读写操作,从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等导致的内存访问问题,保证了并发场景下的一致性、原子性和有序性。

    内存模型解决并发问题主要采用两种方式:限制处理器优化使用内存屏障。本文就不深入底层原理来展开介绍了,感兴趣的朋友可以自行学习。

     

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725111959965?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    什么是Java内存模型

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000117?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    前面介绍过了计算机内存模型,这是解决多线程场景下并发问题的一个重要规范。那么具体的实现是如何的呢,不同的编程语言,在实现上可能有所不同。

    我们知道,Java程序是需要运行在Java虚拟机上面的,Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。

    提到Java内存模型,一般指的是JDK 5 开始使用的新的内存模型,主要由JSR-133: JavaTM Memory Model and Thread Specification 描述。感兴趣的可以参看下这份PDF文档(http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf)

    Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

    而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000467?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    这里面提到的主内存和工作内存,读者可以简单的类比成计算机内存模型中的主存和缓存的概念。特别需要注意的是,主内存和工作内存与JVM内存结构中的Java堆、栈、方法区等并不是同一个层次的内存划分,无法直接类比。《深入理解Java虚拟机》中认为,如果一定要勉强对应起来的话,从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分。工作内存则对应于虚拟机栈中的部分区域。

    所以,再来总结下,JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。目的是保证并发编程场景中的原子性、可见性和有序性。

     

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000242?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    Java内存模型的实现

    ![在这里插入图片描述](https://img-blog.csdn.net/20180725112000339?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3lhb3l1YW5ibw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    了解Java多线程的朋友都知道,在Java中提供了一系列和并发处理相关的关键字,比如volatilesynchronizedfinalconcurren包等。其实这些就是Java内存模型封装了底层的实现后提供给程序员使用的一些关键字。

    在开发多线程的代码的时候,我们可以直接使用synchronized等关键字来控制并发,从来就不需要关心底层的编译器优化、缓存一致性等问题。所以,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。

    本文并不准备把所有的关键字逐一介绍其用法,因为关于各个关键字的用法,网上有很多资料。读者可以自行学习。本文还有一个重点要介绍的就是,我们前面提到,并发编程要解决原子性、有序性和一致性的问题,我们就再来看下,在Java中,分别使用什么方式来保证。

    原子性

    在Java中,为了保证原子性,提供了两个高级的字节码指令monitorentermonitorexit。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized

    因此,在Java中可以使用synchronized来保证方法和代码块内的操作是原子性的。

    可见性

    Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存作为传递媒介的方式来实现的。

    Java中的volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次是用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。

    除了volatile,Java中的synchronizedfinal两个关键字也可以实现可见性。只不过实现方式不同,这里不再展开了。

    有序性

    在Java中,可以使用synchronizedvolatile来保证多线程之间操作的有序性。实现方式有所区别:

    volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只允许一条线程操作。

    好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可以使用的关键字。读者可能发现了,好像synchronized关键字是万能的,他可以同时满足以上三种特性,这其实也是很多人滥用synchronized的原因。

    但是synchronized是比较影响性能的,虽然编译器提供了很多锁优化技术,但是也不建议过度使用。

    总结

    在读完本文之后,相信你应该了解了什么是Java内存模型、Java内存模型的作用以及Java中内存模型做了什么事情等。

    关于Java中这些和内存模型有关的关键字,希望读者还可以继续深入学习,并且自己写几个例子亲自体会一下。可以参考《深入理解Java虚拟机》和《Java并发编程的艺术》两本书。

    展开全文
  • 其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory。时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存free项会很少,此时查看系统的 /proc/meminfo 文件,...
  • linuxwindows不同,会存在缓存内存,通常叫做Cache Memory。有些时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存会很少,此时查看Linux系统 /proc/meminfo 文件,会发现一项 Cached Memory...
  • 其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做CacheMemory。时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存free项会很少,此时查看系统的 /proc/meminfo 文件,会...
  • 在操作数据库时需要构造 sqlSession对象,在对象中一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。一级缓存的作用域是同一个SqlSe...
  • 其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做CacheMemory。 时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存free项会很少,此时查看系统的 /proc/meminfo 文件,...
  • 其实LinuxWin的内存管理不同,会尽量缓存内存以提高读写性能,通常叫做Cache Memory。 时候你会发现没有什么程序在运行,但是使用top或free命令看到可用内存free项会很少,此时查看系统的 /proc/meminfo 文件,...
  • 问:Redis和memcache都是常用的缓存工具,但他们哪些不同你知道吗?平常应用中怎么选择呢? 先说结论,我认为他们以下几方面的不同: 1、Redis和Memcache都是将数据存放在内存中,memcache还可用于缓存其他...
  • 五、Mybati缓存(一级缓存与二级...在操作数据库时需要构造sqlSession对象,在对象中个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的SqlSession之间缓存数据区域 (HashMap)是互不影响的。 2.作用域 一级
  • MemcachedRedis有什么区别

    万次阅读 2020-07-24 11:18:52
    Redis 和 Memcached 都是基于内存的数据存储系统。Memcached是高性能分布式内存缓存...那么,MemcachedRedis有什么区别呢?让我们一起来看一下。 1、数据操作不同 Memcached仅支持简单的key-value结构的数据记录不
  • 1.1什么是查询缓存 ...在操作数据库时需要构造 sqlSession对象,在对象中一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 一级缓...
  • 现在的处理器都是多核处理器,并且每个核都带多个缓存,为什么需要缓存,是因为cpu的速度特别快,比内存快100数量级,所以在cpu与内存之间加了个缓存用来提高访问速度。现在假如一个数在内存里,这个数他会被...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 236
精华内容 94
关键字:

内存与缓存有什么不同