首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >WeakHashMap理解

WeakHashMap理解

作者头像
全栈程序员站长
发布于 2022-09-04 05:17:40
发布于 2022-09-04 05:17:40
54800
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

一、什么是WeakHashMap?

从名字可以得知主要和Map有关,不过还有一个Weak,我们就更能自然而然的想到这里面还牵扯到一种弱引用结构,因此想要彻底搞懂,我们还需要知道四种引用。如果你已经知道了,可以跳过。

1、四种引用

在jvm中,一个对象如果不再被使用就会被当做垃圾给回收掉,判断一个对象是否是垃圾,通常有两种方法:引用计数法和可达性分析法。不管是哪一种方法判断一个对象是否是垃圾的条件总是一个对象的引用是都没有了。

JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用、软引用、弱引用、虚引用4 种。而我们的WeakHashMap就是基于弱引用。

(1)强引用

如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。比如String str = “hello”这时候str就是一个强引用。

(2)软引用

内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。

(3)弱引用

如果一个对象具有弱引用,在垃圾回收时候,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。

(4)虚引用

如果一个对象具有虚引用,就相当于没有引用,在任何时候都有可能被回收。使用虚引用的目的就是为了得知对象被GC的时机,所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。

我们的WeakHashMap是基于弱引用的,也就是说只要垃圾回收机制一开启,就直接开始了扫荡,看见了就清除。

二、为什么需要WeakHashMap

WeakHashMap正是由于使用的是弱引用,因此它的对象可能被随时回收。更直观的说,当使用 WeakHashMap 时,即使没有删除任何元素,它的尺寸、get方法也可能不一样。比如:

(1)调用两次size()方法返回不同的值;第一次为10,第二次就为8了。

(2)两次调用isEmpty()方法,第一次返回false,第二次返回true;

(3)两次调用containsKey()方法,第一次返回true,第二次返回false;

(4)两次调用get()方法,第一次返回一个value,第二次返回null;

是不是觉得有点恶心,这种飘忽不定的东西好像没什么用,试想一下,你准备使用WeakHashMap保存一些数据,写着写着都没了,那还保存个啥呀。

不过有一种场景,最喜欢这种飘忽不定、一言不合就删除的东西。那就是缓存。在缓存场景下,由于内存是有限的,不能缓存所有对象,因此就需要一定的删除机制,淘汰掉一些对象。

现在我们已经知道了WeakHashMap是基于弱引用,其对象可能随时被回收,适用于缓存的场景。下面我们就来看看,WeakHashMap是如何实现这些功能。

三、WeakHashMap工作原理

1、WeakHashMap为什么具有弱引用的特点:随时被回收对象

这个问题就比较简单了,我们的目的主要是验证。WeakHashMap是基于弱引用的,肯定就具有了弱引用的性质。我们去他的源码中看一下:

从这里我们可以看到其内部的Entry继承了WeakReference,也就是弱引用,所以就具有了弱引用的特点。不过还要注意一点,那就是ReferenceQueue,他的作用是GC会清理掉对象之后,引用对象会被放到ReferenceQueue中。

2、WeakHashMap中的Entry被GC后,WeakHashMap是如何将其移除的?

意思是某一个Entry突然被垃圾回收了,这之后WeakHashMap肯定就不能保留这个Entry了,那他是如何将其移除的呢?

WeakHashMap内部有一个expungeStaleEntries函数,在这个函数内部实现移除其内部不用的entry从而达到的自动释放内存的目的。因此我们每次访问WeakHashMap的时候,都会调用这个expungeStaleEntries函数清理一遍。这也就是为什么前两次调用WeakHashMap的size()方法有可能不一样的原因。我们可以看看是如何实现的:

首先GC每次清理掉一个对象之后,引用对象会被放到ReferenceQueue中。然后遍历这个queue进行删除即可。

当然。WeakHashMap的增删改查操作都会直接或者间接的调用expungeStaleEntries()方法,达到及时清除过期entry的目的。

四、WeakHashMap的关键实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>

Entry继承自WeakReference(弱引用),那么Entry本身就是一个弱引用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Entry(Object key, V value,
          ReferenceQueue<Object> queue,
          int hash, Entry<K,V> next) {
        super(key, queue);
        this.value = value;
        this.hash  = hash;
        this.next  = next;
    }

从Entry的构造函数中可以看出:Entry通过传入key和queue调用了父类WeakReference的构造函数,那么key就成为了这个弱引用所引用的对象,并把这个弱引用注册到了引用队列上。

image.png

如果一个对象只具有弱引用,那就类似于可有可无的生活用品。只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

因为存储在Entry中的key只具有弱引用,所以并不能阻止垃圾回收线程对它进行回收,当发生垃圾回收时,Entry中的key被回收,java虚拟机就会把这个Entry添加到与之关联的queue中去。

通过上面的分析,存储在WeakHashMap中的key随时都会面临被回收的风险,因此每次查询WeakHashMap时,都要确认当前WeakHashMap是否已经有key被回收了。当key被回收时,引用这个key的Entry对象就会被添加到引用队列中去,所以只要查询引用队列是否有Entry对象,就可以确认是否有key被回收了。WeakHashMap通过调用expungeStaleEntries方法来清除已经被回收的key所关联的Entry对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void expungeStaleEntries() {
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>) x;
            int i = indexFor(e.hash, table.length);

            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        table[i] = next;
                    else
                        prev.next = next;
                    // Must not null out e.next;
                    // stale entries may be in use by a HashIterator
                    e.value = null; // Help GC
                    size--;
                    break;
                }
                prev = p;
                p = next;
            }
        }
    }
}

WeakHashMap在调用putget方法之前,都会调用expungeStaleEntries方法来清除已经被回收的key所关联的Entry对象。因为Entry是弱引用,即使引用着key对象,但是依然不能阻止垃圾回收线程对key对象的回收。

如果存放在WeakHashMap中的key都存在强引用,那么WeakHashMap就会退化成HashMap。如果在系统中希望通过WeakHashMap自动清除数据,请尽量不要在系统的其他地方强引用WeakHashMap的key,否则,这些key就不会被回收,WeakHashMap也就无法正常释放它们所占用的表项。

五、案例应用

如果在一个普通的HashMap中存储一些比较大的值如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Integer,Object> map = new HashMap<>();
for(int i=0;i<10000;i++)
{
    Integer ii = new Integer(i);
    map.put(ii, new byte[i]);
}

运行参数:-Xmx5M 运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at collections.WeakHashMapTest.main(WeakHashMapTest.java:39)

如果我们将HashMap换成WeakHashMap其余都不变:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Integer,Object> map = new WeakHashMap<>();
for(int i=0;i<10000;i++)
{
    Integer ii = new Integer(i);
    map.put(ii, new byte[i]);
}

运行结果:(无任何报错)

这两段代码比较可以看到WeakHashMap的功效,如果在系统中需要一张很大的Map表,Map中的表项作为缓存使用,这也意味着即使没能从该Map中取得相应的数据,系统也可以通过候选方案获取这些数据。虽然这样会消耗更多的时间,但是不影响系统的正常运行。

在这种场景下,使用WeakHashMap是最合适的。因为WeakHashMap会在系统内存范围内,保存所有表项,而一旦内存不够,在GC时,没有被引用的表项又会很快被清除掉,从而避免系统内存溢出。

我们这里稍微改变一下上面的代码(加了一个List):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Integer,Object> map = new WeakHashMap<>();
List<Integer> list = new ArrayList<>();
for(int i=0;i<10000;i++)
{
    Integer ii = new Integer(i);
    list.add(ii);
    map.put(ii, new byte[i]);
}   

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at collections.WeakHashMapTest.main(WeakHashMapTest.java:43)

如果存放在WeakHashMap中的key都存在强引用,那么WeakHashMap就会退化成HashMap。如果在系统中希望通过WeakHashMap自动清除数据,请尽量不要在系统的其他地方强引用WeakHashMap的key,否则,这些key就不会被回收,WeakHashMap也就无法正常释放它们所占用的表项。

要想WeakHashMap能够释放掉被回收的key关联的value对象,要尽可能的多调用下put/size/get等操作,因为这些方法会调用expungeStaleEntries方法,expungeStaleEntries方法是关键,而如果不操作WeakHashMap,以企图WeakHashMap“自动”释放内存是不可取的,这里的“自动”是指譬如:map.put(obj, new byte[10M]);之后obj=null了,之后再也没调用过map的任何方法,那么new出来的10M空间是不会释放的。

注意

WeakHashMap的key可以为null,那么当put一个key为null,value为一个很大对象的时候,这个很大的对象怎么采用WeakHashMap的自带功能自动释放呢?

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Object,Object> map = new WeakHashMap<>();
map.put(null,new byte[5*1024*928]);
int i = 1;
while(true)
{
    System.out.println();
    TimeUnit.SECONDS.sleep(2);
    System.out.println(map.size());
    System.gc();
    System.out.println("==================第"+i+++"次GC结束====================");
}

运行参数:-Xmx5M -XX:+PrintGCDetails 运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1
[GC [PSYoungGen: 680K->504K(2560K)] 5320K->5240K(7680K), 0.0035741 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 504K->403K(2560K)] [ParOldGen: 4736K->4719K(5120K)] 5240K->5123K(7680K) [PSPermGen: 2518K->2517K(21504K)], 0.0254473 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] 
==================1GC结束====================

1
[Full GC [PSYoungGen: 526K->0K(2560K)] [ParOldGen: 4719K->5112K(5120K)] 5246K->5112K(7680K) [PSPermGen: 2520K->2520K(21504K)], 0.0172785 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
==================2GC结束====================

1
[Full GC [PSYoungGen: 41K->0K(2560K)] [ParOldGen: 5112K->5112K(5120K)] 5153K->5112K(7680K) [PSPermGen: 2520K->2520K(21504K)], 0.0178421 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
==================3GC结束====================

1
[Full GC [PSYoungGen: 41K->0K(2560K)] [ParOldGen: 5112K->5112K(5120K)] 5153K->5112K(7680K) [PSPermGen: 2520K->2520K(21504K)], 0.0164874 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
==================4GC结束====================

1
[Full GC [PSYoungGen: 41K->0K(2560K)] [ParOldGen: 5112K->5112K(5120K)] 5153K->5112K(7680K) [PSPermGen: 2520K->2520K(21504K)], 0.0191096 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
==================5GC结束====================
(一直循环下去)

可以看到在map.put(null, new byte[5*1024*928]);之后,相应的内存一直没有得到释放。

通过显式的调用map.remove(null)可以将内存释放掉,如下代码所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Map<Integer,Object> map = new WeakHashMap<>();
System.gc();
System.out.println("===========gc:1=============");
map.put(null,new byte[4*1024*1024]);
TimeUnit.SECONDS.sleep(5);
System.gc();
System.out.println("===========gc:2=============");
TimeUnit.SECONDS.sleep(5);
System.gc();
System.out.println("===========gc:3=============");
map.remove(null);
TimeUnit.SECONDS.sleep(5);
System.gc();
System.out.println("===========gc:4=============");

运行参数:-Xmx5M -XX:+PrintGCDetails 运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[GC [PSYoungGen: 720K->504K(2560K)] 720K->544K(6144K), 0.0023652 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 504K->0K(2560K)] [ParOldGen: 40K->480K(3584K)] 544K->480K(6144K) [PSPermGen: 2486K->2485K(21504K)], 0.0198023 secs] [Times: user=0.11 sys=0.00, real=0.02 secs] 
===========gc:1=============
[GC [PSYoungGen: 123K->32K(2560K)] 4699K->4608K(7680K), 0.0026722 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 32K->0K(2560K)] [ParOldGen: 4576K->4578K(5120K)] 4608K->4578K(7680K) [PSPermGen: 2519K->2519K(21504K)], 0.0145734 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
===========gc:2=============
[GC [PSYoungGen: 40K->32K(2560K)] 4619K->4610K(7680K), 0.0013068 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 32K->0K(2560K)] [ParOldGen: 4578K->4568K(5120K)] 4610K->4568K(7680K) [PSPermGen: 2519K->2519K(21504K)], 0.0189642 secs] [Times: user=0.06 sys=0.00, real=0.02 secs] 
===========gc:3=============
[GC [PSYoungGen: 40K->32K(2560K)] 4609K->4600K(7680K), 0.0011742 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC [PSYoungGen: 32K->0K(2560K)] [ParOldGen: 4568K->472K(5120K)] 4600K->472K(7680K) [PSPermGen: 2519K->2519K(21504K)], 0.0175907 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
===========gc:4=============
Heap
 PSYoungGen      total 2560K, used 82K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd14820,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 5120K, used 472K [0x00000000ff800000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 5120K, 9% used [0x00000000ff800000,0x00000000ff876128,0x00000000ffd00000)
 PSPermGen       total 21504K, used 2526K [0x00000000fa600000, 0x00000000fbb00000, 0x00000000ff800000)
  object space 21504K, 11% used [0x00000000fa600000,0x00000000fa8778f8,0x00000000fbb00000)

分析:

1、在WeakHashMap中,put的key为null时,放入的是NULL_KEY,即:private static final Object NULL_KEY = new Object(),是一个静态常量。 2、在WeakHashMap中,由于传给WeakReference的只有key和queue,即gc只回收里面的KEY,而不会动value,value的清除则是在expungeStaleEntries这个私有方法进行的。 3、而static的就不在gc之列,所以key也就不会被gc,所以它的大值value,也就不会被设为null,不会被回收。 4、通过调用remove方法,最终table[k]设为null,此时大对象游离所以被回收。

只有通过remove方法才能删除null键所关联的value,建议在使用WeakHashMap的时候尽量避免使用null作为键。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/138148.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月3,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
1 条评论
热度
最新
现在根本找不到这个镜像!
现在根本找不到这个镜像!
回复回复点赞举报
推荐阅读
如何通过Genstat软件来计算遗传力和配合力?
对于不经常编程的小伙伴,Genstat窗口的选择模型,方差分析、混合线性模型、配合力分析、基因与环境互作等功能,特别适合农业方向的数据分析。这个软件功能很强大,通过鼠标点击的形式就可以进行复杂模型的分析,非常666。
邓飞
2024/03/25
6830
如何通过Genstat软件来计算遗传力和配合力?
育种中一般配合力和特殊配合力的计算方法
研究生期间,学习数量遗传学时,对配合力的概念感到很神奇,曾经和同学讨论过,按道理说曹操的一般配合力很高,几个孩子都很出色。
邓飞
2019/12/05
1.7K0
NCII试验 计算配合力和遗传力的方法
育种中,有几个必须要掌握的概念,配合力是其中之一。配合力包括一般配合力和特殊配合力。这个概念很抽象,下面用曹操的例子,解释一下。
邓飞
2022/12/13
4270
NCII试验 计算配合力和遗传力的方法
GS在植物育种中的成本和遗传增益分析
文章的亮点是基因组选择在植物育种中的优势分析, 从时间成本, 金钱成本, 效益成本进行分析, 今天看来还是有很强的参考性.
邓飞
2019/06/13
1.3K0
如何使用NC II 遗传设计估算配合力和遗传力?
非常令人疑惑,于是我就多看了一些教材,发现是有一个万能公式的,即是考虑近交系数,整体结论:
邓飞
2020/08/24
1.6K0
如何使用NC II 遗传设计估算配合力和遗传力?
如何计算配合力+方差组分+遗传力
育种中,有几个必须要掌握的概念,配合力是其中之一。配合力包括一般配合力和特殊配合力。这个概念很抽象,下面用曹操的例子,解释一下。
邓飞
2022/12/13
6920
如何计算配合力+方差组分+遗传力
我的农学转数据分析之花花草草
数据分析界育种知识最好、育种界编程最扎实、段子讲得最好的数据分析师,所以:编程+数据分析+育种,就是我的日常工作了。
邓飞
2022/12/12
5650
我的农学转数据分析之花花草草
通过基因组选择预测杂交水稻的表现(数据挖掘)
基因组选择,预测杂种优势,在水稻可以用,在玉米,高粱中也可以用,在动物选择配套系时也可以用,根据加性效应和非加性效应进行预测,前景广阔。
邓飞
2019/06/13
1K0
GLMM:广义线性混合模型(遗传参数评估)
这篇文章,主要是介绍了抗性数据,如何利用GLMM模型进行的分析,文中,他将9级分类性状变为了二分类性状,进行分析。
邓飞
2020/09/30
2.2K0
GLMM:广义线性混合模型(遗传参数评估)
全同胞家系如何计算遗传力及育种值
大家好,我是飞哥,今天是农历七月初七,中国的情人节,祝大家节日快乐,有情人终成眷属,单身的尽快脱单。
邓飞
2022/12/13
8490
全同胞家系如何计算遗传力及育种值
当我们在说方差分析时,我们在说些什么?
方差分析或变方分析(Analysis of variance,简称ANOVA)为数据分析中常见的统计模型,主要为探讨连续型(Continuous)资料型态之因变量(Dependent variable)与类别型资料型态之自变量(Independent variable)的关系,当自变项的因子中包含等于或超过三个类别情况下,检定其各类别间平均数是否相等的统计模式,广义上可将T检定中方差相等(Equality of variance)的合并T检定(Pooled T-test)视为是方差分析的一种,基于T检定为分析两组平均数是否相等,并且采用相同的计算概念,而实际上当方差分析套用在合并T检定的分析上时,产生的F值则会等于T检定的平方项。
邓飞
2019/09/25
1.6K0
当我们在说方差分析时,我们在说些什么?
文献阅读: 林木中遗传参数评估
这篇文章, 非常具有代表性, 可以为林木和作物的数据分析提供思路, 不一定非要有系谱才可以计算育种值和遗传相关, 混合线性模型代替一般线性模型进行育种值的筛选, 是大势所趋, 必须推而广之.
邓飞
2019/06/13
6620
统计遗传学:第二章,统计分析概念
前几天推荐了这本书,可以领取pdf和配套数据代码。这里,我将各个章节介绍一下,总结也是学习的过程。
邓飞
2022/07/27
8000
统计遗传学:第二章,统计分析概念
环境遗传相关 | 育种中的基因与环境互作
基因与环境互作,植物中同一个品种多年多点种植,评价基因与环境互作,找到品种最适合推广的区域。
邓飞
2022/12/13
1.1K0
环境遗传相关 | 育种中的基因与环境互作
完结篇 | GWAS计算BLUE值4--联合方差分析演示
本篇,用书籍中的数据和结论,用R语言的一般线性模型和混合线性模型,做一下一年多点的联合方差分析的演示。
邓飞
2021/12/20
8110
完结篇 | GWAS计算BLUE值4--联合方差分析演示
多年多点数据如何计算遗传力以及BLUP值
之前写过一篇博客, 介绍领导安利我哔哩哔哩的故事, 介绍到我将我从YouTube上收集的关于混合线性模型, 关于GWAS, 关于GS, 关于农业数据分析相关的视频, 上传到了哔哩哔哩上面. 今天我们看一下介绍多年多点遗传力及BLUP值计算的视频内容. 阅读原文可以查看视频, 这里我用文字和代码进行重演.
邓飞
2019/06/13
4.9K1
GWAS计算BLUE值3--LMM考虑残差异质计算BLUE值
本节,介绍如何使用R语言的asreml包拟合混合线性模型,定义残差异质,计算最佳线性无偏估计(blue)
邓飞
2021/12/20
8960
GWAS计算BLUE值3--LMM考虑残差异质计算BLUE值
动物育种统计发展的百年--翻译版
Daniel Gianola1–5 and Guilherme J.M. Rosa1,2
邓飞
2023/09/06
4040
动物育种统计发展的百年--翻译版
农学的为何要学习神经网络???
在我刚开始学习GS的时候,我是从混合线性模型(LMM)的基础上理解的,因为动物模型BLUP,所以基因组选择GBLUP,再所以一步法ssGBLUP。
邓飞
2022/12/13
3430
农学的为何要学习神经网络???
MP长篇综述 | 植物泛基因组及其应用
2022年12月15日,中山大学史俊鹏副教授、中国科学院遗传与发育生物学研究所田志喜研究员、中国农业大学赖锦盛教授和上海师范大学黄学辉教授共同撰文,在Molecular Plant杂志发表了题为“Plant pan-genomics and its applications”的长篇综述。该论文对植物中泛基因组产生的源动力、表征、分析方法以及在植物遗传学和育种中的应用进行了系统总结和展望。
生信宝典
2023/08/30
6920
MP长篇综述 | 植物泛基因组及其应用
推荐阅读
相关推荐
如何通过Genstat软件来计算遗传力和配合力?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档