<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
            <title type="text">个人信息展示</title>
    <updated>2021-04-01T14:44:32+08:00</updated>
        <id>http://www.ttendeavor.com</id>
        <link rel="alternate" type="text/html" href="http://www.ttendeavor.com" />
        <link rel="self" type="application/atom+xml" href="http://www.ttendeavor.com/atom.xml" />
    <rights>Copyright © 2026, 个人信息展示</rights>
    <generator uri="https://halo.run/" version="1.6.0">Halo</generator>
            <entry>
                <title><![CDATA[JVM垃圾回收算法和内存分代模型]]></title>
                <link rel="alternate" type="text/html" href="http://www.ttendeavor.com/archives/jvm垃圾回收算法和内存分代模型" />
                <id>tag:http://www.ttendeavor.com,2021-04-01:jvm垃圾回收算法和内存分代模型</id>
                <published>2021-04-01T14:43:40+08:00</published>
                <updated>2021-04-01T14:44:32+08:00</updated>
                <author>
                    <name>Don Wang</name>
                    <uri>http://www.ttendeavor.com</uri>
                </author>
                <content type="html">
                        <![CDATA[<h1 id="什么是垃圾">什么是垃圾</h1><p>想要做垃圾回收首先要定位什么是jvm垃圾？<br />垃圾就是没有任何引用指向的一个对象或者多个对象为垃圾。<br />没有任何引用指向的一个对象我们好理解，什么是没有任何引用指向的多个对象呢？<br />我在此举两个例子：<br />（1）A对象引用B对象，B对象引用C对象，C对象由引用A对象，但是此处A/B/C对象在他们三个之外没有被引用，这样A/B/C三个对象都被定义为垃圾。这就是循环引用。<br />（2）A引用B，B引用C，C引用D，此处A/B/C/D都没有在这四个之外被引用，那么这4个对象都被定义为垃圾，这就是链式引用。</p><h1 id="如何定位垃圾">如何定位垃圾</h1><p>垃圾被定位用到两个算法：<br />1.引用计数(ReferenceCount)<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-14415b3f13850135.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />引用计数就是一个对象被引用一次计数就加1，引用被释放一次就减1，直到统计引用次数为0,则定义此对象没有被引用是垃圾。<br />Python使用的是引用计数的回收算法，而Java的HotSpot不是。Java的效率比Python的效率高一些，应该有一部分原因是因为HotSpot的回收算法效率好。<br />对于引用计数来说，有一个重大漏洞：无法处理循环引用下的对象。<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-cb157ea0f8cc6486.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />2.根可达算法(RootSearching)<br />根可达算法是将一系列的GC Root对象定义为初始存活对象集合，所有能被GCRoot引用或者关联到的对象都定义为存活对象，否则宣告其死亡定义为垃圾。<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-79b1c21030a713e2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />可以作为GC Roots的对象包括下面几种：<br />(1)JVM虚拟机栈（Main方法栈帧中的局部变量区，也叫做局部变量表）中引用的对象。<br />(2)方法区中的类静态属性引用的对象。<br />(3)方法区中常量引用的对象。<br />(4)本地方法栈中JNI(Native方法)引用的对象。<br />(5)load进来的Clazz对象</p><h1 id="常见的垃圾回收算法">常见的垃圾回收算法</h1><p>1.标记清除(mark-sweep)<br />简而言之就是把死亡对象的内存空间标记为空闲，并记录在一个空闲列表（free list）之中。当需要新建对象时，内存管理模块便会从该空闲列表中寻找空闲内存，并划分给新建的对象。<br />优点：简单，直接<br />缺点:<br />(1)造成内存碎片。<br />由于 Java 虚拟机的堆中对象必须是连续分布的，因此可能出现总空闲内存足够，但是无法分配的极端情况。比如：总空间100M，此时我们需要申请100M的数组。但是由于内存不连续，因此我们就会申请失败。<br />(2) 分配效率较低。<br />如果是一块连续的内存空间，那么我们可以通过指针加法（pointer bumping）来做分配。而对于空闲列表，Java 虚拟机则需要逐个访问列表中的项，来查找能够放入新建对象的空闲内存。<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-9b10f5646c15c586.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />2.拷贝算法(copying)<br />拷贝算法就是把内存区域分为两等分，分别用两个指针 from 和 to 来维护，并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时，便把存活的对象复制到 to 指针指向的内存区域中，并且交换 from 指针和 to 指针的内容。<br />优点：没有碎片；效率高<br />缺点：浪费空间（占用实际空间的2倍）<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-6ff9957ba554916f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />3.标记压缩(mark-compact)<br />标记压缩法是标记出死亡对象为空闲空间，并且在回收的时候一边整理一边压缩空间，把所有存活的空间整理到从起始位置开始的连续空间内。<br />优点：不会产生碎片化的空间<br />缺点：由于需要边整理边压缩，因此性能开销会比拷贝算法大<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-a3097406c7a1a2ea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /><br />#jvm内存分代模型（用于分代垃圾回收算法）</p><ol><li>部分垃圾回收器使用的模型<br />除Epsilon  ZGC  Shenandoah 之外的GC都是使用逻辑分代模型<br />G1是逻辑分代，物理部分代<br />除此之外不仅逻辑分代，物理也分代</li><li>新生代+老年代+永久代（tenured）（1.7）Perm Generation/ 元数据区（1.8）Metaspace<br />新生代:老年代默认比例1:2<br />a.永久代 元数据 -class<br />b.永久代必须指定大小限制，元数据可以设置，也可以不设置，无上限（受限于物理内存）<br />c.字符串常量 1.7 - 永久代， 1.8- 堆<br />d.MethodArea逻辑概念 - 永久代、元数据</li><li>新生代 = Eden + 2个suvivor区<br />a. YGC回收之后，大多数的对象会被回收，活着的进入s0<br />b. 再次YGC，活着的对象eden + s0 -&gt; s1<br />c. 再次YGC，eden + s1 -&gt; s0<br />d. 年龄足够 -&gt; 老年代 （15 CMS 6）<br />e. s区装不下 -&gt; 老年代</li><li>老年代<br />a. 顽固分子<br />b. 老年代满了FGC Full GC</li><li>GC Tuning (Generation)<br />a. 尽量减少FGC<br />b. MinorGC = YGC<br />c. MajorGC = FGC</li><li>对象分配过程图<br /><img src="https://upload-images.jianshu.io/upload_images/18122061-47a0170c035c0244.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png" /></li><li>动态年龄<br />年龄从小到大进行累加，当加入某个年龄段后，累加和超过survivor区域*TargetSurvivorRatio的时候，就从这个年龄段网上的年龄的对象进行晋升。<br />具体参考：<a href="https://www.jianshu.com/p/989d3b06a49d">https://www.jianshu.com/p/989d3b06a49d</a></li><li>分配担保<br />当在新生代无法分配内存的时候，把新生代的对象转移到老生代，然后把新对象放入腾空的新生代。<br />具体参考：<a href="https://cloud.tencent.com/developer/article/1082730">https://cloud.tencent.com/developer/article/1082730</a></li></ol>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[2021-03-26-14-57-38]]></title>
                <link rel="alternate" type="text/html" href="http://www.ttendeavor.com/archives/自研springboot连接redis并动态切换database多redistemplate方法" />
                <id>tag:http://www.ttendeavor.com,2021-03-26:自研springboot连接redis并动态切换database多redistemplate方法</id>
                <published>2021-03-26T14:57:39+08:00</published>
                <updated>2021-03-26T15:03:00+08:00</updated>
                <author>
                    <name>Don Wang</name>
                    <uri>http://www.ttendeavor.com</uri>
                </author>
                <content type="html">
                        <![CDATA[<h1 id="前要">前要</h1><p>手头有个项目会用到查询redis同数据源不同database的情况，在网上找了一堆方法感觉在多线程高并发下都会有一些问题。由此，自己研究出了此种方法希望能帮助到大家。<br />话不多说下面贴代码了开始 O(∩_∩)O哈哈~</p><h1 id="自定义redisproperties">自定义RedisProperties</h1><p>自定义redisProperties实现添加dbList项</p><pre><code class="language-java">package com.dong.base.redis.core;import org.apache.commons.lang3.StringUtils;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import java.util.List;/***  自定义redis 配置类* @author 王少东*/public class MyRedisProperties extends RedisProperties {    // 需要操作的db    private String dbList;    public String[] getDbList() {        if (StringUtils.isEmpty(dbList)) {            return  null;        }        String[] list =  dbList.split(&quot;,&quot;);        return list;    }    public void setDbList(String dbList) {        this.dbList = dbList;    }}</code></pre><h1 id="自定义stringredistemplate">自定义StringRedisTemplate</h1><p>自定义SringRedisTemplate实现存储待操作的db的redisTemplate列表</p><pre><code class="language-java">package com.dong.base.redis.core;import io.lettuce.core.resource.ClientResources;import io.lettuce.core.resource.DefaultClientResources;import lombok.Data;import org.apache.commons.lang3.StringUtils;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.util.Assert;import java.util.HashMap;import java.util.Map;/*** 自定义StringRedisTemplate类* @author 王少东*/@Datapublic class MyStringRedisTemplate extends StringRedisTemplate {    // 需要切换db的StringRedisTemplate 的 map    private Map&lt;String, StringRedisTemplate&gt; redisTemplateMap = new HashMap();    // 需要切换db的ClientResources    private ClientResources dbListClientResources;    // 需要切换db的MyRedisProperties    private MyRedisProperties dbListProperties;    public MyStringRedisTemplate(){    }    public MyStringRedisTemplate(MyRedisProperties properties){        this(properties,DefaultClientResources.create());    }    public MyStringRedisTemplate(MyRedisProperties properties, ClientResources clientResources){        this(properties,clientResources,null);    }    public MyStringRedisTemplate(MyRedisProperties properties, ClientResources clientResources,RedisConnectionFactory redisConnectionFactory){        this.dbListProperties = properties;        this.dbListClientResources = clientResources;        if (redisConnectionFactory != null) {            this.setConnectionFactory(redisConnectionFactory);        }        this.buildMoreDbStringRedisTemple();    }    /**    * 获取某个数据库的RedisTemplate    * @param dbIndex    * @return    */    public StringRedisTemplate getDbStringRedisTemplate(String dbIndex) {        StringRedisTemplate template = this.redisTemplateMap.get(dbIndex);        Assert.isTrue(template != null, dbIndex + &quot;库不存在！&quot;);        return template;    }    public StringRedisTemplate getDbStringRedisTemplate(int dbIndex) {        return getDbStringRedisTemplate(String.valueOf(dbIndex));    }    // 构建待切换db列表    private void buildMoreDbStringRedisTemple () {        if (dbListProperties != null )        if (this.dbListProperties.getDbList() != null &amp;&amp; this.dbListProperties.getDbList().length &gt; 0) {            for (String db : this.dbListProperties.getDbList()) {                Assert.isTrue(StringUtils.isNumeric(db), &quot;redis 配置的dblist 必须是数字！&quot;);                // 当默认和默认db一样的时候设置类本身                if (db.equals(String.valueOf(this.dbListProperties.getDatabase()))) {                    redisTemplateMap.put(db,this);                    continue;                }                StringRedisTemplate template = new StringRedisTemplate();                // 构建 LettuceConnectionFactory 连接池                LettuceConnectionFactory lettuceConnectionFactory = BuildRedisConfig.buildLettuceConnectionFactory(Integer.valueOf(db), dbListClientResources,this.dbListProperties);                template.setConnectionFactory(lettuceConnectionFactory);                lettuceConnectionFactory.afterPropertiesSet();                template.afterPropertiesSet();                redisTemplateMap.put(db,template);            }        }    }}</code></pre><h1 id="buildredisconfig类">BuildRedisConfig类</h1><p>BuildRedisConfig是一个构建LettuceConnectionFactory的工具类</p><pre><code class="language-java">package com.dong.base.redis.core;import io.lettuce.core.resource.ClientResources;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.RedisStandaloneConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;/*** 公共构建redis BuildRedisConfig的类* @author 王少东*/public class BuildRedisConfig {    /**    * 构建LettuceConnectionFactory的方法    * @param clientResources    * @param properties    * @return    */    public static LettuceConnectionFactory buildLettuceConnectionFactory(ClientResources clientResources, RedisProperties properties){      return buildLettuceConnectionFactory(-1,clientResources, properties );    }    /**    * 构建LettuceConnectionFactory的方法    * @param clientResources    * @param properties    * @return    */    public static LettuceConnectionFactory buildLettuceConnectionFactory(int dbIndex, ClientResources clientResources, RedisProperties properties){        RedisStandaloneConfiguration redis1StandaloneConfiguration = getStandaloneConfig(dbIndex, properties);        LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources, properties);        return new LettuceConnectionFactory(redis1StandaloneConfiguration, clientConfig);    }    /**    * redis standalone config    *    * @param redisProperties redis 配置参数    * @return RedisStandaloneConfiguration    */    private static RedisStandaloneConfiguration getStandaloneConfig(int dbIndex, RedisProperties redisProperties) {        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();        config.setHostName(redisProperties.getHost());        config.setPort(redisProperties.getPort());        config.setPassword(RedisPassword.of(redisProperties.getPassword()));        config.setDatabase(dbIndex &gt; -1 ? dbIndex : redisProperties.getDatabase());        return config;    }    /**    * redis standalone config    *    * @param redisProperties redis 配置参数    * @return RedisStandaloneConfiguration    */    private static RedisStandaloneConfiguration getStandaloneConfig(RedisProperties redisProperties) {        return getStandaloneConfig(-1, redisProperties);    }    /**    * 构建 LettuceClientConfiguration    *    * @param clientResources clientResources    * @param redisProperties redisProperties    * @return LettuceClientConfiguration    */    private static LettuceClientConfiguration getLettuceClientConfiguration(ClientResources clientResources,                                                                    RedisProperties redisProperties) {        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder =                createBuilder(redisProperties.getLettuce().getPool());        if (redisProperties.isSsl()) {            builder.useSsl();        }        if (redisProperties.getTimeout() != null) {            builder.commandTimeout(redisProperties.getTimeout());        }        if (redisProperties.getLettuce() != null) {            RedisProperties.Lettuce lettuce = redisProperties.getLettuce();            if (lettuce.getShutdownTimeout() != null                    &amp;&amp; !lettuce.getShutdownTimeout().isZero()) {                builder.shutdownTimeout(                        redisProperties.getLettuce().getShutdownTimeout());            }        }        builder.clientResources(clientResources);        return builder.build();    }    /**    * 创建 LettuceClientConfigurationBuilder    *    * @param pool 连接池配置    * @return LettuceClientConfigurationBuilder    */    private static LettuceClientConfiguration.LettuceClientConfigurationBuilder createBuilder(RedisProperties.Pool pool) {        if (pool == null) {            return LettuceClientConfiguration.builder();        }        return LettucePoolingClientConfiguration.builder()                .poolConfig(getPoolConfig(pool));    }    /**    * pool config    *    * @param properties redis 参数配置    * @return GenericObjectPoolConfig    */    private static GenericObjectPoolConfig getPoolConfig(RedisProperties.Pool properties) {        GenericObjectPoolConfig config = new GenericObjectPoolConfig();        config.setMaxTotal(properties.getMaxActive());        config.setMaxIdle(properties.getMaxIdle());        config.setMinIdle(properties.getMinIdle());        if (properties.getMaxWait() != null) {            config.setMaxWaitMillis(properties.getMaxWait().toMillis());        }        return config;    }}</code></pre><h1 id="redisconfig-配置类">RedisConfig 配置类</h1><pre><code class="language-java">package com.dong.base.redis.config;import com.dong.base.redis.core.BuildRedisConfig;import com.dong.base.redis.core.MyRedisProperties;import com.dong.base.redis.core.MyStringRedisTemplate;import io.lettuce.core.resource.ClientResources;import io.lettuce.core.resource.DefaultClientResources;import lombok.Getter;import lombok.Setter;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.data.redis.RedisProperties;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Primary;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.RedisStandaloneConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;import org.springframework.data.redis.core.StringRedisTemplate;@Configuration@ConfigurationProperties(prefix = &quot;spring.redis&quot;)@Getter@Setterpublic class RedisConfig {    private MyRedisProperties master;    private MyRedisProperties cache;    @Bean(destroyMethod = &quot;shutdown&quot;)    @ConditionalOnMissingBean(ClientResources.class)    public DefaultClientResources lettuceClientResources() {        return DefaultClientResources.create();    }    @Bean(name = &quot;redisTemplateMaster&quot;)    @Primary    public MyStringRedisTemplate stringRedisTemplate1(            @Qualifier(&quot;RedisConnectionFactoryMaster&quot;) RedisConnectionFactory redisConnectionFactory,ClientResources clientResources) {//        StringRedisTemplate template = new StringRedisTemplate();//        template.setConnectionFactory(redisConnectionFactory);        MyStringRedisTemplate template = new MyStringRedisTemplate(master,clientResources,redisConnectionFactory);        return template;    }    @Bean(name = &quot;RedisConnectionFactoryMaster&quot;)    @Primary    public LettuceConnectionFactory Redis1LettuceConnectionFactory(ClientResources clientResources) {        return BuildRedisConfig.buildLettuceConnectionFactory(clientResources,master);    }    @Bean(name = &quot;redisTemplateCache&quot;)    public MyStringRedisTemplate stringRedisTemplate2(            @Qualifier(&quot;RedisConnectionFactoryCache&quot;) RedisConnectionFactory redisConnectionFactory) {        MyStringRedisTemplate template = new MyStringRedisTemplate();        template.setConnectionFactory(redisConnectionFactory);        return template;    }    @Bean(name = &quot;RedisConnectionFactoryCache&quot;)    public LettuceConnectionFactory Redis2LettuceConnectionFactory(ClientResources clientResources) {        return BuildRedisConfig.buildLettuceConnectionFactory(clientResources,cache);    }}</code></pre><h1 id="applicationyml配置文件">application.yml配置文件</h1><p>在原来的默认redis配置文件中加入了dbList配置字，此处我用到了多数据源自行更改</p><pre><code class="language-yaml">spring:  redis:    master:#      redis 持久化地址      host: 127.0.0.1      port: 6379      password: 123132      database: 15      dbList: 0,1,2,3,4,5,6,7,8,9    cache:#      redis 缓存库的地址      host: 127.0.0.1      port: 7379      password: 1231      database: 0</code></pre><h1 id="用法">用法</h1><p>MyStringRedisTemplate 直接操作走的默认数据（注：就是database配置）</p><p>MyStringRedisTemplate.getDbStringRedisTemplate(5)获取到需要操作的db的redisTemplate再去操作redis。</p>]]>
                </content>
            </entry>
</feed>
