You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

653 lines
130 KiB

<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"><meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.0"><link rel="preconnect" href="https://fonts.googleapis.com" crossorigin><link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png"><link rel="mask-icon" href="/images/logo.svg" color="#222"><meta name="google-site-verification" content="2X6S9P7CAjXjVvw8YyQR8pCu-B0oEu7O9quLgxXuWyA"><meta name="baidu-site-verification" content="dV8JGNzi0c"><script>setTimeout(function(){var d=document.createElement("script");d.setAttribute("data-ad-client","ca-pub-7480618470784058"),d.async=!0,d.src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js",document.body.appendChild(d)},5e3),window.addEventListener?window.addEventListener("load",downloadJsAtOnload,!1):window.attachEvent?window.attachEvent("onload",downloadJsAtOnload):window.onload=downloadJsAtOnload</script><link rel="stylesheet" href="/css/main.css"><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.15.3/css/all.min.css" integrity="sha256-2H3fkXt6FEmrReK448mDVGKb3WW2ZZw35gI7vqHOE4Y=" crossorigin="anonymous"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" integrity="sha256-Vzbj7sDDS/woiFS3uNKo8eIuni59rjyNGtXfstRzStA=" crossorigin="anonymous"><script class="next-config" data-name="main" type="application/json">{"hostname":"nicksxs.me","root":"/","images":"/images","scheme":"Pisces","version":"8.6.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":true,"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":true,"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"motion":{"enable":false,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果:${query}","hits_time":"找到 ${hits} 个搜索结果(用时 ${time} 毫秒)","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":true,"preload":true}}</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/config.min.js"></script><meta name="description" content="learn from zero,技术博客,Nicksxs,史学森"><meta property="og:type" content="website"><meta property="og:title" content="Nicksxs&#39;s Blog"><meta property="og:url" content="https://nicksxs.me/page/24/index.html"><meta property="og:site_name" content="Nicksxs&#39;s Blog"><meta property="og:description" content="learn from zero,技术博客,Nicksxs,史学森"><meta property="og:locale" content="zh_CN"><meta property="article:author" content="Nicksxs"><meta property="article:tag" content="Nicksxs,史学森,米方方,米方方的男朋友,森哥"><meta name="twitter:card" content="summary"><link rel="canonical" href="https://nicksxs.me/page/24/"><script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/24/index.html","title":""}</script><script class="next-config" data-name="calendar" type="application/json">""</script><title>Nicksxs's Blog</title><script async src="https://www.googletagmanager.com/gtag/js?id=UA-61358619-1"></script><script class="next-config" data-name="google_analytics" type="application/json">{"tracking_id":"UA-61358619-1","only_pageview":false}</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/analytics/google-analytics.min.js"></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/analytics/baidu-analytics.min.js"></script><script async src="https://hm.baidu.com/hm.js?20f33b3c0c0eff9b1522999c0015646d"></script><noscript><link rel="stylesheet" href="/css/noscript.css"></noscript><link rel="alternate" href="/atom.xml" title="Nicksxs's Blog" type="application/atom+xml"></head><body itemscope itemtype="http://schema.org/WebPage"><div class="headband"></div><main class="main"><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-container"><div class="site-nav-toggle"><div class="toggle" aria-label="切换导航栏" role="button"><span class="toggle-line"></span> <span class="toggle-line"></span> <span class="toggle-line"></span></div></div><div class="site-meta"><a href="/" class="brand" rel="start"><i class="logo-line"></i><h1 class="site-title">Nicksxs's Blog</h1><i class="logo-line"></i></a><p class="site-subtitle" itemprop="description">What hurts more, the pain of hard work or the pain of regret?</p></div><div class="site-nav-right"><div class="toggle popup-trigger"><i class="fa fa-search fa-fw fa-lg"></i></div></div></div><nav class="site-nav"><ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li><li class="menu-item menu-item-top"><a href="/top/" rel="section"><i class="fa fa-th fa-fw"></i>热度</a></li><li class="menu-item menu-item-sitemap"><a href="/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-commonweal"><a href="/404/" rel="section"><i class="fa fa-heartbeat fa-fw"></i>公益 404</a></li><li class="menu-item menu-item-search"><a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索</a></li></ul></nav><div class="search-pop-overlay"><div class="popup search-popup"><div class="search-header"><span class="search-icon"><i class="fa fa-search"></i></span><div class="search-input-container"><input autocomplete="off" autocapitalize="off" maxlength="80" placeholder="搜索..." spellcheck="false" type="search" class="search-input"></div><span class="popup-btn-close" role="button"><i class="fa fa-times-circle"></i></span></div><div class="search-result-container no-result"><div class="search-result-icon"><i class="fa fa-spinner fa-pulse fa-5x"></i></div></div></div></div></div><div class="toggle sidebar-toggle" role="button"><span class="toggle-line"></span> <span class="toggle-line"></span> <span class="toggle-line"></span></div><aside class="sidebar"><div class="sidebar-inner sidebar-overview-active"><ul class="sidebar-nav"><li class="sidebar-nav-toc">文章目录</li><li class="sidebar-nav-overview">站点概览</li></ul><div class="sidebar-panel-container"><div class="post-toc-wrap sidebar-panel"></div><div class="site-overview-wrap sidebar-panel"><div class="site-overview"><div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="site-author-image" itemprop="image" alt="Nicksxs" src="/uploads/avatar.jpg"><p class="site-author-name" itemprop="name">Nicksxs</p><div class="site-description" itemprop="description">learn from zero,技术博客,Nicksxs,史学森</div></div><div class="site-state-wrap site-overview-item animated"><nav class="site-state"><div class="site-state-item site-state-posts"><a href="/archives/"><span class="site-state-item-count">141</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"><a href="/categories/"><span class="site-state-item-count">139</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"><a href="/tags/"><span class="site-state-item-count">264</span> <span class="site-state-item-name">标签</span></a></div></nav></div><div class="links-of-author site-overview-item animated"><span class="links-of-author-item"><a href="https://github.com/nicksxs" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;nicksxs" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a> </span><span class="links-of-author-item"><a href="mailto:nicksxs1202@gmail.com" title="E-Mail → mailto:nicksxs1202@gmail.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a></span></div><div class="cc-license site-overview-item animated" itemprop="license"><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="https://cdn.jsdelivr.net/npm/@creativecommons/vocabulary@2020.11.3/assets/license_badges/small/by_nc_sa.svg" alt="Creative Commons"></a></div><script charset="utf-8" src="/js/tagcloud.js"></script><script charset="utf-8" src="/js/tagcanvas.js"></script><div class="widget-wrap"><div id="myCanvasContainer" class="widget tagcloud"><canvas width="250" height="250" id="resCanvas"><ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/2019/" rel="tag">2019</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/2020/" rel="tag">2020</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/2021/" rel="tag">2021</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/2PC/" rel="tag">2PC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/360-%E5%85%A8%E5%AE%B6%E6%A1%B6/" rel="tag">360 全家桶</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/3PC/" rel="tag">3PC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/AOP/" rel="tag">AOP</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Adaptive/" rel="tag">Adaptive</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apollo/" rel="tag">Apollo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/AutoConfiguration/" rel="tag">AutoConfiguration</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Binary-Tree/" rel="tag">Binary Tree</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Broker/" rel="tag">Broker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/" rel="tag">C</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/" rel="tag">C++</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/CachedThreadPool/" rel="tag">CachedThreadPool</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Comparator/" rel="tag">Comparator</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DFS/" rel="tag">DFS</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DP/" rel="tag">DP</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DefaultMQPushConsumer/" rel="tag">DefaultMQPushConsumer</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Design-Patterns/" rel="tag">Design Patterns</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Distributed-Lock/" rel="tag">Distributed Lock</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Docker/" rel="tag">Docker</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Dockerfile/" rel="tag">Dockerfile</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Druid/" rel="tag">Druid</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Dubbo/" rel="tag">Dubbo</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/EagerThreadPool/" rel="tag">EagerThreadPool</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Evict/" rel="tag">Evict</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Filter/" rel="tag">Filter</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/FixedThreadPool/" rel="tag">FixedThreadPool</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/G1/" rel="tag">G1</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/GC/" rel="tag">GC</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Garbage-First-Collector/" rel="tag">Garbage-First Collector</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Gogs/" rel="tag">Gogs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Homebrew/" rel="tag">Homebrew</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Inorder-Traversal/" rel="tag">Inorder Traversal</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Interceptor/" rel="tag">Interceptor</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JMap/" rel="tag">JMap</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JPS/" rel="tag">JPS</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JStack/" rel="tag">JStack</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JVM/" rel="tag">JVM</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Java/" rel="tag">Java</a><span class="tag-list-count">24</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Leetcode-42/" rel="tag">Leetcode 42</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/LimitedThreadPool/" rel="tag">LimitedThreadPool</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Linked-List/" rel="tag">Linked List</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Lowest-Common-Ancestor-of-a-Binary-Tree/" rel="tag">Lowest Common Ancestor of a Binary Tree</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/MQ/" rel="tag">MQ</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Mac/" rel="tag">Mac</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Maven/" rel="tag">Maven</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Mybatis/" rel="tag">Mybatis</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Mysql/" rel="tag">Mysql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/NameServer/" rel="tag">NameServer</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/PHP/" rel="tag">PHP</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Preorder-Traversal/" rel="tag">Preorder Traversal</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RPC/" rel="tag">RPC</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Redis/" rel="tag">Redis</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/RocketMQ/" rel="tag">RocketMQ</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Rotate-Image/" rel="tag">Rotate Image</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Rust/" rel="tag">Rust</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SPI/" rel="tag">SPI</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Servlet/" rel="tag">Servlet</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Sharding-Jdbc/" rel="tag">Sharding-Jdbc</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Singleton/" rel="tag">Singleton</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Spring/" rel="tag">Spring</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/SpringBoot/" rel="tag">SpringBoot</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Sql%E6%B3%A8%E5%85%A5/" rel="tag">Sql注入</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Stream/" rel="tag">Stream</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Synchronized/" rel="tag">Synchronized</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Thread-dump/" rel="tag">Thread dump</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ThreadLocal/" rel="tag">ThreadLocal</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ThreadPool/" rel="tag">ThreadPool</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Tomcat/" rel="tag">Tomcat</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Trapping-Rain-Water/" rel="tag">Trapping Rain Water</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/WeakReference/" rel="tag">WeakReference</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Web/" rel="tag">Web</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Webhook/" rel="tag">Webhook</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/WordPress/" rel="tag">WordPress</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/aqs/" rel="tag">aqs</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/await/" rel="tag">await</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/bloom-filter/" rel="tag">bloom filter</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/c/" rel="tag">c++</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cglib/" rel="tag">cglib</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cgroup/" rel="tag">cgroup</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cluster/" rel="tag">cluster</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/condition/" rel="tag">condition</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/" rel="tag">docker</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/dp/" rel="tag">dp</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/echo/" rel="tag">echo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/environment/" rel="tag">environment</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gap-lock/" rel="tag">gap lock</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gc/" rel="tag">gc</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/grep/" rel="tag">grep</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hadoop/" rel="tag">hadoop</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/icu4c/" rel="tag">icu4c</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/im/" rel="tag">im</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/is-not-null/" rel="tag">is not null</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/is-null/" rel="tag">is null</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/j-u-c/" rel="tag">j.u.c</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/" rel="tag">java</a><span class="tag-list-count">19</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jvm/" rel="tag">jvm</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/leetcode/" rel="tag">leetcode</a><span class="tag-list-count">28</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/leetcode-155/" rel="tag">leetcode 155</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linked-list/" rel="tag">linked list</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/" rel="tag">linux</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/lock/" rel="tag">lock</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mfc/" rel="tag">mfc</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/min-stack/" rel="tag">min stack</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mq/" rel="tag">mq</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mvcc/" rel="tag">mvcc</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mysql/" rel="tag">mysql</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/namespace/" rel="tag">namespace</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/next-key-lock/" rel="tag">next-key lock</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/" rel="tag">nginx</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nullsfirst/" rel="tag">nullsfirst</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/openresty/" rel="tag">openresty</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/php/" rel="tag">php</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/procedure/" rel="tag">procedure</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/python/" rel="tag">python</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/read-view/" rel="tag">read view</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/" rel="tag">redis</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/signal/" rel="tag">signal</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sort/" rel="tag">sort</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spark/" rel="tag">spark</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ssh/" rel="tag">ssh</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/stack/" rel="tag">stack</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/string/" rel="tag">string</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/swoole/" rel="tag">swoole</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/top/" rel="tag">top</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/uname/" rel="tag">uname</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/unlock/" rel="tag">unlock</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/utf8/" rel="tag">utf8</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/utf8mb4/" rel="tag">utf8mb4</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/utf8mb4-0900-ai-ci/" rel="tag">utf8mb4_0900_ai_ci</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/utf8mb4-general-ci/" rel="tag">utf8mb4_general_ci</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/utf8mb4-unicode-ci/" rel="tag">utf8mb4_unicode_ci</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/value/" rel="tag">value</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/websocket/" rel="tag">websocket</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zsh/" rel="tag">zsh</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%89%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/" rel="tag">三阶段提交</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%8D%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/" rel="tag">不可变引用</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%9C%E4%BA%AC%E5%A5%A5%E8%BF%90%E4%BC%9A/" rel="tag">东京奥运会</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%A4%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4/" rel="tag">两阶段提交</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%AD%E5%B1%B1%E8%B7%AF/" rel="tag">中山路</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%AD%E5%BA%8F/" rel="tag">中序</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%AD%E9%97%B4%E4%BB%B6/" rel="tag">中间件</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B8%BE%E9%87%8D/" rel="tag">举重</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%B9%92%E4%B9%93%E7%90%83/" rel="tag">乒乓球</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BA%8B%E5%8A%A1/" rel="tag">事务</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BA%8C%E5%8F%89%E6%A0%91/" rel="tag">二叉树</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BA%92%E6%96%A5%E9%94%81/" rel="tag">互斥锁</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BB%A3%E7%A0%81%E9%A2%98%E8%A7%A3/" rel="tag">代码题解</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E4%BF%AE%E7%94%B5%E8%84%91%E7%9A%84/" rel="tag">修电脑的</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%81%8F%E5%90%91%E9%94%81/" rel="tag">偏向锁</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%81%A5%E5%BA%B7%E7%A0%81/" rel="tag">健康码</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%85%AC%E4%BA%A4/" rel="tag">公交</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%85%AC%E4%BA%A4%E8%BD%A6/" rel="tag">公交车</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%86%85%E5%AD%98%E5%88%86%E5%B8%83/" rel="tag">内存分布</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/" rel="tag">内存泄漏</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%87%86%E5%A4%87/" rel="tag">准备</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%87%8F%E8%82%A5/" rel="tag">减肥</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1/" rel="tag">分布式事务</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" rel="tag">分布式锁</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%88%87%E7%89%87/" rel="tag">切片</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%88%9D%E5%A7%8B%E5%8C%96/" rel="tag">初始化</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%89%8A%E5%B3%B0%E5%A1%AB%E8%B0%B7/" rel="tag">削峰填谷</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%89%8D%E5%BA%8F/" rel="tag">前序</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8A%A0%E5%A1%9E/" rel="tag">加塞</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8A%A0%E8%BD%BD/" rel="tag">加载</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8D%95%E4%BE%8B/" rel="tag">单例</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8D%9A%E5%AE%A2%EF%BC%8C%E6%96%87%E7%AB%A0/" rel="tag">博客,文章</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8E%A6%E9%97%A8/" rel="tag">厦门</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE/" rel="tag">双亲委派</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8F%91%E8%A1%8C%E7%89%88/" rel="tag">发行版</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8F%A3%E7%BD%A9/" rel="tag">口罩</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%8F%AF%E5%8F%98%E5%BC%95%E7%94%A8/" rel="tag">可变引用</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%90%90%E6%A7%BD/" rel="tag">吐槽</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/" rel="tag">垃圾回收</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD/" rel="tag">基础设施</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/" rel="tag">字符集</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%AE%89%E5%85%A8/" rel="tag">安全</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%AE%B9%E9%94%99%E6%9C%BA%E5%88%B6/" rel="tag">容错机制</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%AF%84%E7%94%9F%E8%99%AB/" rel="tag">寄生虫</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B0%84%E5%87%BB/" rel="tag">射击</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B0%8F%E6%8A%80%E5%B7%A7/" rel="tag">小技巧</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B1%80%E5%8F%A3%E8%A1%97/" rel="tag">局口街</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8/" rel="tag">布隆过滤器</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B2%E6%B4%BB/" rel="tag">干活</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B4%E4%B8%AD%E6%80%BB%E7%BB%93/" rel="tag">年中总结</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/" rel="tag">年终总结</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B6%E5%8F%91/" rel="tag">并发</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%B8%E7%A6%8F%E4%BA%86%E5%90%97/" rel="tag">幸福了吗</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%B9%BB%E8%AF%BB/" rel="tag">幻读</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%BA%94%E7%94%A8/" rel="tag">应用</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%BC%80%E8%BD%A6/" rel="tag">开车</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%BC%B1%E5%BC%95%E7%94%A8/" rel="tag">弱引用</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E5%BD%B1%E8%AF%84/" rel="tag">影评</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%89%80%E6%9C%89%E6%9D%83/" rel="tag">所有权</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%89%93%E5%8D%A1/" rel="tag">打卡</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%89%B6%E6%A2%AF/" rel="tag">扶梯</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%8A%80%E6%9C%AF/" rel="tag">技术</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%8B%96%E6%9B%B4/" rel="tag">拖更</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%8E%92%E5%BA%8F/" rel="tag">排序</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%8E%A5%E9%9B%A8%E6%B0%B4/" rel="tag">接雨水</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%95%B0%E6%8D%AE%E6%BA%90%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2/" rel="tag">数据源动态切换</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" rel="tag">数据结构</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%96%B0%E8%AF%AD%E8%A8%80/" rel="tag">新语言</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%97%85%E6%B8%B8/" rel="tag">旅游</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%9B%BE%E5%8E%9D%E5%9E%B5/" rel="tag">曾厝垵</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%9C%80%E5%B0%8F%E6%A0%88/" rel="tag">最小栈</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%9D%80%E4%BA%BA%E8%AF%9B%E5%BF%83/" rel="tag">杀人诛心</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%9D%AD%E5%B7%9E/" rel="tag">杭州</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86/" rel="tag">标记整理</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%A4%8D%E7%89%A9%E5%9B%AD/" rel="tag">植物园</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B2%99%E8%8C%B6%E9%9D%A2/" rel="tag">沙茶面</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B3%A8%E8%A7%A3/" rel="tag">注解</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B5%B7%E8%9B%8E%E7%85%8E/" rel="tag">海蛎煎</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" rel="tag">消息队列</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%B7%98%E6%B1%B0%E7%AD%96%E7%95%A5/" rel="tag">淘汰策略</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%BA%90%E7%A0%81/" rel="tag">源码</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/" rel="tag">源码解析</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%94%9F%E6%B4%BB/" rel="tag">生活</a><span class="tag-list-count">29</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%94%B5%E7%93%B6%E8%BD%A6/" rel="tag">电瓶车</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%96%AB%E6%83%85/" rel="tag">疫情</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%9C%8B%E4%B9%A6/" rel="tag">看书</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%9C%8B%E5%89%A7/" rel="tag">看剧</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%9F%A9%E9%98%B5/" rel="tag">矩阵</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/" rel="tag">端口转发</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%B1%BB%E5%8A%A0%E8%BD%BD/" rel="tag">类加载</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%B3%9F%E5%BF%83%E4%BA%8B/" rel="tag">糟心事</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%B4%A2%E5%BC%95/" rel="tag">索引</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BA%BF%E7%A8%8B%E6%B1%A0/" rel="tag">线程池</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BC%93%E5%AD%98/" rel="tag">缓存</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF/" rel="tag">缓存击穿</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F/" rel="tag">缓存穿透</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9/" rel="tag">缓存雪崩</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BC%96%E7%A0%81/" rel="tag">编码</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E7%BE%8E%E5%9B%BD/" rel="tag">美国</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%80%81%E7%94%B5%E8%84%91/" rel="tag">老电脑</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/" rel="tag">自动装配</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%87%AA%E6%97%8B/" rel="tag">自旋</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%87%AA%E9%80%82%E5%BA%94%E6%8B%93%E5%B1%95/" rel="tag">自适应拓展</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%A3%85%E7%94%B5%E8%84%91/" rel="tag">装电脑</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%A7%84%E5%88%99/" rel="tag">规则</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%A7%A3%E6%9E%90/" rel="tag">解析</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag">设计模式</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%AF%BB%E4%B9%A6/" rel="tag">读书</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%AF%BB%E5%90%8E%E6%84%9F/" rel="tag">读后感</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%B6%B3%E7%90%83/" rel="tag">足球</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%B7%91%E6%AD%A5/" rel="tag">跑步</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%B7%AF%E6%94%BF%E8%A7%84%E5%88%92/" rel="tag">路政规划</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%B7%B3%E6%B0%B4/" rel="tag">跳水</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%B8%A9%E8%B8%8F/" rel="tag">踩踏</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%BD%AC%E4%B9%89/" rel="tag">转义</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81/" rel="tag">轻量级锁</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%BF%87%E6%9C%9F%E7%AD%96%E7%95%A5/" rel="tag">过期策略</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%BF%90%E5%8A%A8/" rel="tag">运动</a><span class="tag-list-count">8</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%80%92%E5%BD%92/" rel="tag">递归</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%87%8D%E9%87%8F%E7%BA%A7%E9%94%81/" rel="tag">重量级锁</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%93%BE%E6%8E%A5/" rel="tag">链接</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%A2%98%E8%A7%A3/" rel="tag">题解</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%A9%AC%E6%88%8F%E5%9B%A2/" rel="tag">马戏团</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%AA%8C%E8%AF%81/" rel="tag">验证</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%AA%91%E8%BD%A6/" rel="tag">骑车</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%AB%98%E9%80%9F/" rel="tag">高速</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E9%BC%93%E6%B5%AA%E5%B1%BF/" rel="tag">鼓浪屿</a><span class="tag-list-count">1</span></li></ul></canvas></div></div><div class="links-of-blogroll site-overview-item animated"><div class="links-of-blogroll-title"><i class="fa fa-globe fa-fw"></i> Links</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"><a href="https://covermusic.cn/" title="https:&#x2F;&#x2F;covermusic.cn" rel="noopener" target="_blank">69伙伴</a></li></ul></div></div></div></div></div></aside><div class="sidebar-dimmer"></div></header><div class="back-to-top" role="button" aria-label="返回顶部"><i class="fa fa-arrow-up"></i> <span>0%</span></div><a href="https://github.com/nicksxs" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin:130px 106px" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><noscript><div class="noscript-warning">Theme NexT works best with JavaScript enabled</div></noscript><div class="main-inner index posts-expand"><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content"><link itemprop="mainEntityOfPage" href="https://nicksxs.me/2019/12/10/Redis-Part-1/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/uploads/avatar.jpg"><meta itemprop="name" content="Nicksxs"><meta itemprop="description" content="learn from zero,技术博客,Nicksxs,史学森"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Nicksxs's Blog"></span><header class="post-header"><h2 class="post-title" itemprop="name headline"><a href="/2019/12/10/Redis-Part-1/" class="post-title-link" itemprop="url">Redis_分布式锁</a></h2><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间:2019-12-10 23:26:26" itemprop="dateCreated datePublished" datetime="2019-12-10T23:26:26+08:00">2019-12-10</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Redis/Distributed-Lock/" itemprop="url" rel="index"><span itemprop="name">Distributed Lock</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/C/" itemprop="url" rel="index"><span itemprop="name">C</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/C/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a> </span></span><span id="/2019/12/10/Redis-Part-1/" class="post-meta-item leancloud_visitors" data-flag-title="Redis_分布式锁" title="阅读次数"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数:</span> <span class="leancloud-visitors-count"></span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">Disqus:</span> <a title="disqus" href="/2019/12/10/Redis-Part-1/#disqus_thread" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="2019/12/10/Redis-Part-1/" itemprop="commentCount"></span></a></span></div></div></header><div class="post-body" itemprop="articleBody"><p>今天看了一下 <code>redis</code> 分布式锁 <code>redlock</code> 的实现,简单记录下,</p><h2 id="加锁"><a href="#加锁" class="headerlink" title="加锁"></a>加锁</h2><p>原先我对 <code>redis</code> 锁的概念就是加锁使用 <code>setnx</code>,解锁使用 <code>lua</code> 脚本,但是 <code>setnx</code> 具体是啥,<code>lua</code> 脚本是啥不是很清楚<br>首先简单思考下这个问题,首先为啥不是先 <code>get</code> 一下 <code>key</code> 存不存在,然后再 <code>set</code> 一个 <code>key value</code>,因为加锁这个操作我们是要保证两点,一个是不能中途被打断,也就是说要原子性,如果是先 <code>get</code> 一下 <code>key</code>,如果不存在再 <code>set</code> 值的话,那就不是原子操作了;第二个是可不可以直接 <code>set</code> 值呢,显然不行,锁要保证唯一性,有且只能有一个线程或者其他应用单位获得该锁,正好 <code>setnx</code> 给了我们这种原子命令</p><p>然后是 <code>setnx</code> 的键和值分别是啥,键比较容易想到是要锁住的资源,比如 <code>user_id</code>, 这里有个我自己之前比较容易陷进去的误区,但是这个误区后<br>面再说,这里其实是把<code>user_id</code> 作为要锁住的资源,在我获得锁的时候别的线程不允许操作,以此保证业务的正确性,不会被多个线程同时修改,确定了键,再来看看值是啥,其实原先我认为值是啥都没关系,我只要锁住了,光键就够我用了,但是考虑下多个线程的问题,如果我这个线程加了锁,然后我因为 <code>gc</code> 停顿等原因卡死了,这个时候<code>redis</code> 的锁或者说就是 <code>redis</code> 的缓存已经过期了,这时候另一个线程获得锁成功,然后我这个线程又活过来了,然后我就仍然认为我拿着锁,我去对数据进行修改或者释放锁,是不是就出现问题了,所以是不是我们还需要一个东西来区分这个锁是哪个线程加的,所以我们可以将值设置成为一个线程独有识别的值,至少在相对长的一段时间内不会重复。</p><p>上面其实还有两个问题,一个是当 <code>gc</code> 超时时,我这个线程如何知道我手里的锁已经过期了,一种方法是我在加好锁之后就维护一个超时时间,这里其实还有个问题,不过跟第二个问题相关,就一起说了,就是设置超时时间,有些对于不是锁的 <code>redis</code> 缓存操作可以是先设置好值,然后在设置过期时间,那么这就又有上面说到的不是原子性的问题,那么就需要在同一条指令里把超时时间也设置了,幸好 <code>redis</code> 提供了这种支持</p><pre class="line-numbers language-none"><code class="language-none">SET resource_name my_random_value NX PX 30000<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这里借鉴一下解释下,<code>resource_name</code>就是 key,代表要锁住的东西,<code>my_random_value</code>就是识别我这个线程的,<code>NX</code>代表只有在不存在的时候才设置,然后<code>PX 30000</code>表示超时时间是 30秒自动过期</p><p>PS:记录下我原先有的一个误区,是不是要用 key 来区分加锁的线程,这样只有一个用处,就是自身线程可以识别是否是自己加的锁,但是最大的问题是别的线程不知道,其实这个用户的出发点是我在担心前面提过的一个问题,就是当 gc 停顿后,我要去判断当前的这个锁是否是我加的,还有就是当释放锁的时候,如果保证不会错误释放了其他线程加的锁,但是这样附带很多其他问题,最大的就是其他线程怎么知道能不能加这个锁。</p><h2 id="解锁"><a href="#解锁" class="headerlink" title="解锁"></a>解锁</h2><p>当线程在锁过期之前就处理完了业务逻辑,那就可以提前释放这个锁,那么提前释放要怎么操作,直接<code>del key</code>显然是不行的,因为这样就是我前面想用线程随机值加资源名作为锁的初衷,我不能去释放别的线程加的锁,那么我要怎么办呢,先 <code>get</code> 一下看是不是我的?那又变成非原子的操作了,幸好redis 也考虑到了这个问题,给了<code>lua</code> 脚本来操作这种</p><pre class="line-numbers language-lua" data-language="lua"><code class="language-lua"><span class="token keyword">if</span> redis<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token string">"get"</span><span class="token punctuation">,</span>KEYS<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">==</span> ARGV<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">then</span>
<span class="token keyword">return</span> redis<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token string">"del"</span><span class="token punctuation">,</span>KEYS<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token keyword">else</span>
<span class="token keyword">return</span> <span class="token number">0</span>
<span class="token keyword">end</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里的<code>KEYS[1]</code>就是前面加锁的<code>resource_name</code>,<code>ARGV[1]</code>就是线程的随机值<code>my_random_value</code></p><h2 id="多节点"><a href="#多节点" class="headerlink" title="多节点"></a>多节点</h2><p>前面说的其实是单节点 <code>redis</code> 作为分布式锁的情况,那么当我们的 <code>redis</code> 有多节点的情况呢,如果多节点下处于加锁或者解锁或者锁有效情况下<br><code>redis</code> 的某个节点宕掉了怎么办,这里就有一些需要思考的地方,是否单独搞一个单节点的 <code>redis</code>作为分布式锁专用的,但是如果这个单节点的挂了呢,还有就是成本问题,所以我们需要一个多节点的分布式锁方案<br>这里就引出了开头说到的<code>redlock</code>,这个可是 <code>redis</code>的作者写的, 他的加锁过程是分以下几步去做这个事情</p><ul><li>获取当前时间(毫秒数)。</li><li>按顺序依次向N个Redis节点执行获取锁的操作。这个获取操作跟前面基于单Redis节点的获取锁的过程相同,包含随机字符串my_random_value,也包含过期时间(比如PX 30000,即锁的有效时间)。为了保证在某个Redis节点不可用的时候算法能够继续运行,这个获取锁的操作还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个Redis节点获取锁失败以后,应该立即尝试下一个Redis节点。这里的失败,应该包含任何类型的失败,比如该Redis节点不可用,或者该Redis节点上的锁已经被其它客户端持有(注:Redlock原文中这里只提到了Redis节点不可用的情况,但也应该包含其它的失败情况)。</li><li>计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。如果客户端从大多数Redis节点(&gt;= N/2+1)成功获取到了锁,并且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;否则,认为最终获取锁失败。</li><li>如果最终获取锁成功了,那么这个锁的有效时间应该重新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。</li><li>如果最终获取锁失败了(可能由于获取到锁的Redis节点个数少于N/2+1,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该立即向所有Redis节点发起释放锁的操作(即前面介绍的Redis Lua脚本)。<br>释放锁的过程比较简单:客户端向所有Redis节点发起释放锁的操作,不管这些节点当时在获取锁的时候成功与否。这里为什么要向所有的节点发送释放锁的操作呢,这里是因为有部分的节点的失败原因可能是加锁时阻塞,加锁成功的结果没有及时返回,所以为了防止这种情况还是需要向所有发起这个释放锁的操作。<br>初步记录就先到这。</li></ul></div><footer class="post-footer"><div class="post-eof"></div></footer></article></div><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content"><link itemprop="mainEntityOfPage" href="https://nicksxs.me/2019/12/07/JVM-G1-Part-1/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/uploads/avatar.jpg"><meta itemprop="name" content="Nicksxs"><meta itemprop="description" content="learn from zero,技术博客,Nicksxs,史学森"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Nicksxs's Blog"></span><header class="post-header"><h2 class="post-title" itemprop="name headline"><a href="/2019/12/07/JVM-G1-Part-1/" class="post-title-link" itemprop="url">JVM源码分析之G1垃圾收集器分析一</a></h2><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间:2019-12-07 00:54:19" itemprop="dateCreated datePublished" datetime="2019-12-07T00:54:19+08:00">2019-12-07</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar-check"></i> </span><span class="post-meta-item-text">更新于</span> <time title="修改时间:2019-12-20 00:01:42" itemprop="dateModified" datetime="2019-12-20T00:01:42+08:00">2019-12-20</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Java/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Java/GC/" itemprop="url" rel="index"><span itemprop="name">GC</span></a> </span><span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/C/" itemprop="url" rel="index"><span itemprop="name">C++</span></a> </span></span><span id="/2019/12/07/JVM-G1-Part-1/" class="post-meta-item leancloud_visitors" data-flag-title="JVM源码分析之G1垃圾收集器分析一" title="阅读次数"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数:</span> <span class="leancloud-visitors-count"></span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">Disqus:</span> <a title="disqus" href="/2019/12/07/JVM-G1-Part-1/#disqus_thread" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="2019/12/07/JVM-G1-Part-1/" itemprop="commentCount"></span></a></span></div></div></header><div class="post-body" itemprop="articleBody"><p>对 Java 的 gc 实现比较感兴趣,原先一般都是看周志明的书,但其实并没有讲具体的 gc 源码,而是把整个思路和流程讲解了一下<br>特别是 G1 的具体实现<br>一般对 G1 的理解其实就是把原先整块的新生代老年代分成了以 region 为单位的小块内存,简而言之,就是原先对新生代老年代的收集会涉及到整个代的堆内存空间,而G1 把它变成了更细致的小块内存<br>这带来了一个很明显的好处和一个很明显的坏处,好处是内存收集可以更灵活,耗时会变短,但整个收集的处理复杂度就变高了<br>目前看了一点点关于 G1 收集的预期时间相关的代码</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">HeapWord* G1CollectedHeap::do_collection_pause(size_t word_size,
uint gc_count_before,
bool* succeeded,
GCCause::Cause gc_cause) &#123;
assert_heap_not_locked_and_not_at_safepoint();
VM_G1CollectForAllocation op(word_size,
gc_count_before,
gc_cause,
false, &#x2F;* should_initiate_conc_mark *&#x2F;
g1_policy()-&gt;max_pause_time_ms());
VMThread::execute(&amp;op);
HeapWord* result &#x3D; op.result();
bool ret_succeeded &#x3D; op.prologue_succeeded() &amp;&amp; op.pause_succeeded();
assert(result &#x3D;&#x3D; NULL || ret_succeeded,
&quot;the result should be NULL if the VM did not succeed&quot;);
*succeeded &#x3D; ret_succeeded;
assert_heap_not_locked();
return result;
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里就是收集时需要停顿的,其中<code>VMThread::execute(&amp;op);</code>是具体执行的,真正执行的是<code>VM_G1CollectForAllocation::doit</code>方法</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">void VM_G1CollectForAllocation::doit() &#123;
G1CollectedHeap* g1h &#x3D; G1CollectedHeap::heap();
assert(!_should_initiate_conc_mark || g1h-&gt;should_do_concurrent_full_gc(_gc_cause),
&quot;only a GC locker, a System.gc(), stats update, whitebox, or a hum allocation induced GC should start a cycle&quot;);
if (_word_size &gt; 0) &#123;
&#x2F;&#x2F; An allocation has been requested. So, try to do that first.
_result &#x3D; g1h-&gt;attempt_allocation_at_safepoint(_word_size,
false &#x2F;* expect_null_cur_alloc_region *&#x2F;);
if (_result !&#x3D; NULL) &#123;
&#x2F;&#x2F; If we can successfully allocate before we actually do the
&#x2F;&#x2F; pause then we will consider this pause successful.
_pause_succeeded &#x3D; true;
return;
&#125;
&#125;
GCCauseSetter x(g1h, _gc_cause);
if (_should_initiate_conc_mark) &#123;
&#x2F;&#x2F; It&#39;s safer to read old_marking_cycles_completed() here, given
&#x2F;&#x2F; that noone else will be updating it concurrently. Since we&#39;ll
&#x2F;&#x2F; only need it if we&#39;re initiating a marking cycle, no point in
&#x2F;&#x2F; setting it earlier.
_old_marking_cycles_completed_before &#x3D; g1h-&gt;old_marking_cycles_completed();
&#x2F;&#x2F; At this point we are supposed to start a concurrent cycle. We
&#x2F;&#x2F; will do so if one is not already in progress.
bool res &#x3D; g1h-&gt;g1_policy()-&gt;force_initial_mark_if_outside_cycle(_gc_cause);
&#x2F;&#x2F; The above routine returns true if we were able to force the
&#x2F;&#x2F; next GC pause to be an initial mark; it returns false if a
&#x2F;&#x2F; marking cycle is already in progress.
&#x2F;&#x2F;
&#x2F;&#x2F; If a marking cycle is already in progress just return and skip the
&#x2F;&#x2F; pause below - if the reason for requesting this initial mark pause
&#x2F;&#x2F; was due to a System.gc() then the requesting thread should block in
&#x2F;&#x2F; doit_epilogue() until the marking cycle is complete.
&#x2F;&#x2F;
&#x2F;&#x2F; If this initial mark pause was requested as part of a humongous
&#x2F;&#x2F; allocation then we know that the marking cycle must just have
&#x2F;&#x2F; been started by another thread (possibly also allocating a humongous
&#x2F;&#x2F; object) as there was no active marking cycle when the requesting
&#x2F;&#x2F; thread checked before calling collect() in
&#x2F;&#x2F; attempt_allocation_humongous(). Retrying the GC, in this case,
&#x2F;&#x2F; will cause the requesting thread to spin inside collect() until the
&#x2F;&#x2F; just started marking cycle is complete - which may be a while. So
&#x2F;&#x2F; we do NOT retry the GC.
if (!res) &#123;
assert(_word_size &#x3D;&#x3D; 0, &quot;Concurrent Full GC&#x2F;Humongous Object IM shouldn&#39;t be allocating&quot;);
if (_gc_cause !&#x3D; GCCause::_g1_humongous_allocation) &#123;
_should_retry_gc &#x3D; true;
&#125;
return;
&#125;
&#125;
&#x2F;&#x2F; Try a partial collection of some kind.
_pause_succeeded &#x3D; g1h-&gt;do_collection_pause_at_safepoint(_target_pause_time_ms);
if (_pause_succeeded) &#123;
if (_word_size &gt; 0) &#123;
&#x2F;&#x2F; An allocation had been requested. Do it, eventually trying a stronger
&#x2F;&#x2F; kind of GC.
_result &#x3D; g1h-&gt;satisfy_failed_allocation(_word_size, &amp;_pause_succeeded);
&#125; else &#123;
bool should_upgrade_to_full &#x3D; !g1h-&gt;should_do_concurrent_full_gc(_gc_cause) &amp;&amp;
!g1h-&gt;has_regions_left_for_allocation();
if (should_upgrade_to_full) &#123;
&#x2F;&#x2F; There has been a request to perform a GC to free some space. We have no
&#x2F;&#x2F; information on how much memory has been asked for. In case there are
&#x2F;&#x2F; absolutely no regions left to allocate into, do a maximally compacting full GC.
log_info(gc, ergo)(&quot;Attempting maximally compacting collection&quot;);
_pause_succeeded &#x3D; g1h-&gt;do_full_collection(false, &#x2F;* explicit gc *&#x2F;
true &#x2F;* clear_all_soft_refs *&#x2F;);
&#125;
&#125;
guarantee(_pause_succeeded, &quot;Elevated collections during the safepoint must always succeed.&quot;);
&#125; else &#123;
assert(_result &#x3D;&#x3D; NULL, &quot;invariant&quot;);
&#x2F;&#x2F; The only reason for the pause to not be successful is that, the GC locker is
&#x2F;&#x2F; active (or has become active since the prologue was executed). In this case
&#x2F;&#x2F; we should retry the pause after waiting for the GC locker to become inactive.
_should_retry_gc &#x3D; true;
&#125;
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里可以看到核心的是<code>G1CollectedHeap::do_collection_pause_at_safepoint</code>这个方法,它带上了目标暂停时间的值</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) &#123;
assert_at_safepoint_on_vm_thread();
guarantee(!is_gc_active(), &quot;collection is not reentrant&quot;);
if (GCLocker::check_active_before_gc()) &#123;
return false;
&#125;
_gc_timer_stw-&gt;register_gc_start();
GCIdMark gc_id_mark;
_gc_tracer_stw-&gt;report_gc_start(gc_cause(), _gc_timer_stw-&gt;gc_start());
SvcGCMarker sgcm(SvcGCMarker::MINOR);
ResourceMark rm;
g1_policy()-&gt;note_gc_start();
wait_for_root_region_scanning();
print_heap_before_gc();
print_heap_regions();
trace_heap_before_gc(_gc_tracer_stw);
_verifier-&gt;verify_region_sets_optional();
_verifier-&gt;verify_dirty_young_regions();
&#x2F;&#x2F; We should not be doing initial mark unless the conc mark thread is running
if (!_cm_thread-&gt;should_terminate()) &#123;
&#x2F;&#x2F; This call will decide whether this pause is an initial-mark
&#x2F;&#x2F; pause. If it is, in_initial_mark_gc() will return true
&#x2F;&#x2F; for the duration of this pause.
g1_policy()-&gt;decide_on_conc_mark_initiation();
&#125;
&#x2F;&#x2F; We do not allow initial-mark to be piggy-backed on a mixed GC.
assert(!collector_state()-&gt;in_initial_mark_gc() ||
collector_state()-&gt;in_young_only_phase(), &quot;sanity&quot;);
&#x2F;&#x2F; We also do not allow mixed GCs during marking.
assert(!collector_state()-&gt;mark_or_rebuild_in_progress() || collector_state()-&gt;in_young_only_phase(), &quot;sanity&quot;);
&#x2F;&#x2F; Record whether this pause is an initial mark. When the current
&#x2F;&#x2F; thread has completed its logging output and it&#39;s safe to signal
&#x2F;&#x2F; the CM thread, the flag&#39;s value in the policy has been reset.
bool should_start_conc_mark &#x3D; collector_state()-&gt;in_initial_mark_gc();
&#x2F;&#x2F; Inner scope for scope based logging, timers, and stats collection
&#123;
EvacuationInfo evacuation_info;
if (collector_state()-&gt;in_initial_mark_gc()) &#123;
&#x2F;&#x2F; We are about to start a marking cycle, so we increment the
&#x2F;&#x2F; full collection counter.
increment_old_marking_cycles_started();
_cm-&gt;gc_tracer_cm()-&gt;set_gc_cause(gc_cause());
&#125;
_gc_tracer_stw-&gt;report_yc_type(collector_state()-&gt;yc_type());
GCTraceCPUTime tcpu;
G1HeapVerifier::G1VerifyType verify_type;
FormatBuffer&lt;&gt; gc_string(&quot;Pause Young &quot;);
if (collector_state()-&gt;in_initial_mark_gc()) &#123;
gc_string.append(&quot;(Concurrent Start)&quot;);
verify_type &#x3D; G1HeapVerifier::G1VerifyConcurrentStart;
&#125; else if (collector_state()-&gt;in_young_only_phase()) &#123;
if (collector_state()-&gt;in_young_gc_before_mixed()) &#123;
gc_string.append(&quot;(Prepare Mixed)&quot;);
&#125; else &#123;
gc_string.append(&quot;(Normal)&quot;);
&#125;
verify_type &#x3D; G1HeapVerifier::G1VerifyYoungNormal;
&#125; else &#123;
gc_string.append(&quot;(Mixed)&quot;);
verify_type &#x3D; G1HeapVerifier::G1VerifyMixed;
&#125;
GCTraceTime(Info, gc) tm(gc_string, NULL, gc_cause(), true);
uint active_workers &#x3D; AdaptiveSizePolicy::calc_active_workers(workers()-&gt;total_workers(),
workers()-&gt;active_workers(),
Threads::number_of_non_daemon_threads());
active_workers &#x3D; workers()-&gt;update_active_workers(active_workers);
log_info(gc,task)(&quot;Using %u workers of %u for evacuation&quot;, active_workers, workers()-&gt;total_workers());
TraceCollectorStats tcs(g1mm()-&gt;incremental_collection_counters());
TraceMemoryManagerStats tms(&amp;_memory_manager, gc_cause(),
collector_state()-&gt;yc_type() &#x3D;&#x3D; Mixed &#x2F;* allMemoryPoolsAffected *&#x2F;);
G1HeapTransition heap_transition(this);
size_t heap_used_bytes_before_gc &#x3D; used();
&#x2F;&#x2F; Don&#39;t dynamically change the number of GC threads this early. A value of
&#x2F;&#x2F; 0 is used to indicate serial work. When parallel work is done,
&#x2F;&#x2F; it will be set.
&#123; &#x2F;&#x2F; Call to jvmpi::post_class_unload_events must occur outside of active GC
IsGCActiveMark x;
gc_prologue(false);
if (VerifyRememberedSets) &#123;
log_info(gc, verify)(&quot;[Verifying RemSets before GC]&quot;);
VerifyRegionRemSetClosure v_cl;
heap_region_iterate(&amp;v_cl);
&#125;
_verifier-&gt;verify_before_gc(verify_type);
_verifier-&gt;check_bitmaps(&quot;GC Start&quot;);
#if COMPILER2_OR_JVMCI
DerivedPointerTable::clear();
#endif
&#x2F;&#x2F; Please see comment in g1CollectedHeap.hpp and
&#x2F;&#x2F; G1CollectedHeap::ref_processing_init() to see how
&#x2F;&#x2F; reference processing currently works in G1.
&#x2F;&#x2F; Enable discovery in the STW reference processor
_ref_processor_stw-&gt;enable_discovery();
&#123;
&#x2F;&#x2F; We want to temporarily turn off discovery by the
&#x2F;&#x2F; CM ref processor, if necessary, and turn it back on
&#x2F;&#x2F; on again later if we do. Using a scoped
&#x2F;&#x2F; NoRefDiscovery object will do this.
NoRefDiscovery no_cm_discovery(_ref_processor_cm);
&#x2F;&#x2F; Forget the current alloc region (we might even choose it to be part
&#x2F;&#x2F; of the collection set!).
_allocator-&gt;release_mutator_alloc_region();
&#x2F;&#x2F; This timing is only used by the ergonomics to handle our pause target.
&#x2F;&#x2F; It is unclear why this should not include the full pause. We will
&#x2F;&#x2F; investigate this in CR 7178365.
&#x2F;&#x2F;
&#x2F;&#x2F; Preserving the old comment here if that helps the investigation:
&#x2F;&#x2F;
&#x2F;&#x2F; The elapsed time induced by the start time below deliberately elides
&#x2F;&#x2F; the possible verification above.
double sample_start_time_sec &#x3D; os::elapsedTime();
g1_policy()-&gt;record_collection_pause_start(sample_start_time_sec);
if (collector_state()-&gt;in_initial_mark_gc()) &#123;
concurrent_mark()-&gt;pre_initial_mark();
&#125;
g1_policy()-&gt;finalize_collection_set(target_pause_time_ms, &amp;_survivor);
evacuation_info.set_collectionset_regions(collection_set()-&gt;region_length());
&#x2F;&#x2F; Make sure the remembered sets are up to date. This needs to be
&#x2F;&#x2F; done before register_humongous_regions_with_cset(), because the
&#x2F;&#x2F; remembered sets are used there to choose eager reclaim candidates.
&#x2F;&#x2F; If the remembered sets are not up to date we might miss some
&#x2F;&#x2F; entries that need to be handled.
g1_rem_set()-&gt;cleanupHRRS();
register_humongous_regions_with_cset();
assert(_verifier-&gt;check_cset_fast_test(), &quot;Inconsistency in the InCSetState table.&quot;);
&#x2F;&#x2F; We call this after finalize_cset() to
&#x2F;&#x2F; ensure that the CSet has been finalized.
_cm-&gt;verify_no_cset_oops();
if (_hr_printer.is_active()) &#123;
G1PrintCollectionSetClosure cl(&amp;_hr_printer);
_collection_set.iterate(&amp;cl);
&#125;
&#x2F;&#x2F; Initialize the GC alloc regions.
_allocator-&gt;init_gc_alloc_regions(evacuation_info);
G1ParScanThreadStateSet per_thread_states(this, workers()-&gt;active_workers(), collection_set()-&gt;young_region_length());
pre_evacuate_collection_set();
&#x2F;&#x2F; Actually do the work...
evacuate_collection_set(&amp;per_thread_states);
post_evacuate_collection_set(evacuation_info, &amp;per_thread_states);
const size_t* surviving_young_words &#x3D; per_thread_states.surviving_young_words();
free_collection_set(&amp;_collection_set, evacuation_info, surviving_young_words);
eagerly_reclaim_humongous_regions();
record_obj_copy_mem_stats();
_survivor_evac_stats.adjust_desired_plab_sz();
_old_evac_stats.adjust_desired_plab_sz();
double start &#x3D; os::elapsedTime();
start_new_collection_set();
g1_policy()-&gt;phase_times()-&gt;record_start_new_cset_time_ms((os::elapsedTime() - start) * 1000.0);
if (evacuation_failed()) &#123;
set_used(recalculate_used());
if (_archive_allocator !&#x3D; NULL) &#123;
_archive_allocator-&gt;clear_used();
&#125;
for (uint i &#x3D; 0; i &lt; ParallelGCThreads; i++) &#123;
if (_evacuation_failed_info_array[i].has_failed()) &#123;
_gc_tracer_stw-&gt;report_evacuation_failed(_evacuation_failed_info_array[i]);
&#125;
&#125;
&#125; else &#123;
&#x2F;&#x2F; The &quot;used&quot; of the the collection set have already been subtracted
&#x2F;&#x2F; when they were freed. Add in the bytes evacuated.
increase_used(g1_policy()-&gt;bytes_copied_during_gc());
&#125;
if (collector_state()-&gt;in_initial_mark_gc()) &#123;
&#x2F;&#x2F; We have to do this before we notify the CM threads that
&#x2F;&#x2F; they can start working to make sure that all the
&#x2F;&#x2F; appropriate initialization is done on the CM object.
concurrent_mark()-&gt;post_initial_mark();
&#x2F;&#x2F; Note that we don&#39;t actually trigger the CM thread at
&#x2F;&#x2F; this point. We do that later when we&#39;re sure that
&#x2F;&#x2F; the current thread has completed its logging output.
&#125;
allocate_dummy_regions();
_allocator-&gt;init_mutator_alloc_region();
&#123;
size_t expand_bytes &#x3D; _heap_sizing_policy-&gt;expansion_amount();
if (expand_bytes &gt; 0) &#123;
size_t bytes_before &#x3D; capacity();
&#x2F;&#x2F; No need for an ergo logging here,
&#x2F;&#x2F; expansion_amount() does this when it returns a value &gt; 0.
double expand_ms;
if (!expand(expand_bytes, _workers, &amp;expand_ms)) &#123;
&#x2F;&#x2F; We failed to expand the heap. Cannot do anything about it.
&#125;
g1_policy()-&gt;phase_times()-&gt;record_expand_heap_time(expand_ms);
&#125;
&#125;
&#x2F;&#x2F; We redo the verification but now wrt to the new CSet which
&#x2F;&#x2F; has just got initialized after the previous CSet was freed.
_cm-&gt;verify_no_cset_oops();
&#x2F;&#x2F; This timing is only used by the ergonomics to handle our pause target.
&#x2F;&#x2F; It is unclear why this should not include the full pause. We will
&#x2F;&#x2F; investigate this in CR 7178365.
double sample_end_time_sec &#x3D; os::elapsedTime();
double pause_time_ms &#x3D; (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS;
size_t total_cards_scanned &#x3D; g1_policy()-&gt;phase_times()-&gt;sum_thread_work_items(G1GCPhaseTimes::ScanRS, G1GCPhaseTimes::ScanRSScannedCards);
g1_policy()-&gt;record_collection_pause_end(pause_time_ms, total_cards_scanned, heap_used_bytes_before_gc);
evacuation_info.set_collectionset_used_before(collection_set()-&gt;bytes_used_before());
evacuation_info.set_bytes_copied(g1_policy()-&gt;bytes_copied_during_gc());
if (VerifyRememberedSets) &#123;
log_info(gc, verify)(&quot;[Verifying RemSets after GC]&quot;);
VerifyRegionRemSetClosure v_cl;
heap_region_iterate(&amp;v_cl);
&#125;
_verifier-&gt;verify_after_gc(verify_type);
_verifier-&gt;check_bitmaps(&quot;GC End&quot;);
assert(!_ref_processor_stw-&gt;discovery_enabled(), &quot;Postcondition&quot;);
_ref_processor_stw-&gt;verify_no_references_recorded();
&#x2F;&#x2F; CM reference discovery will be re-enabled if necessary.
&#125;
#ifdef TRACESPINNING
ParallelTaskTerminator::print_termination_counts();
#endif
gc_epilogue(false);
&#125;
&#x2F;&#x2F; Print the remainder of the GC log output.
if (evacuation_failed()) &#123;
log_info(gc)(&quot;To-space exhausted&quot;);
&#125;
g1_policy()-&gt;print_phases();
heap_transition.print();
&#x2F;&#x2F; It is not yet to safe to tell the concurrent mark to
&#x2F;&#x2F; start as we have some optional output below. We don&#39;t want the
&#x2F;&#x2F; output from the concurrent mark thread interfering with this
&#x2F;&#x2F; logging output either.
_hrm.verify_optional();
_verifier-&gt;verify_region_sets_optional();
TASKQUEUE_STATS_ONLY(print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
print_heap_after_gc();
print_heap_regions();
trace_heap_after_gc(_gc_tracer_stw);
&#x2F;&#x2F; We must call G1MonitoringSupport::update_sizes() in the same scoping level
&#x2F;&#x2F; as an active TraceMemoryManagerStats object (i.e. before the destructor for the
&#x2F;&#x2F; TraceMemoryManagerStats is called) so that the G1 memory pools are updated
&#x2F;&#x2F; before any GC notifications are raised.
g1mm()-&gt;update_sizes();
_gc_tracer_stw-&gt;report_evacuation_info(&amp;evacuation_info);
_gc_tracer_stw-&gt;report_tenuring_threshold(_g1_policy-&gt;tenuring_threshold());
_gc_timer_stw-&gt;register_gc_end();
_gc_tracer_stw-&gt;report_gc_end(_gc_timer_stw-&gt;gc_end(), _gc_timer_stw-&gt;time_partitions());
&#125;
&#x2F;&#x2F; It should now be safe to tell the concurrent mark thread to start
&#x2F;&#x2F; without its logging output interfering with the logging output
&#x2F;&#x2F; that came from the pause.
if (should_start_conc_mark) &#123;
&#x2F;&#x2F; CAUTION: after the doConcurrentMark() call below,
&#x2F;&#x2F; the concurrent marking thread(s) could be running
&#x2F;&#x2F; concurrently with us. Make sure that anything after
&#x2F;&#x2F; this point does not assume that we are the only GC thread
&#x2F;&#x2F; running. Note: of course, the actual marking work will
&#x2F;&#x2F; not start until the safepoint itself is released in
&#x2F;&#x2F; SuspendibleThreadSet::desynchronize().
do_concurrent_mark();
&#125;
return true;
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>往下走就是这一步<code>G1Policy::finalize_collection_set</code>,去处理新生代和老年代</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">void G1Policy::finalize_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) &#123;
double time_remaining_ms &#x3D; _collection_set-&gt;finalize_young_part(target_pause_time_ms, survivor);
_collection_set-&gt;finalize_old_part(time_remaining_ms);
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>这里分别调用了两个方法,可以看到剩余时间是往下传的,来看一下具体的方法</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">double G1CollectionSet::finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors) &#123;
double young_start_time_sec &#x3D; os::elapsedTime();
finalize_incremental_building();
guarantee(target_pause_time_ms &gt; 0.0,
&quot;target_pause_time_ms &#x3D; %1.6lf should be positive&quot;, target_pause_time_ms);
size_t pending_cards &#x3D; _policy-&gt;pending_cards();
double base_time_ms &#x3D; _policy-&gt;predict_base_elapsed_time_ms(pending_cards);
double time_remaining_ms &#x3D; MAX2(target_pause_time_ms - base_time_ms, 0.0);
log_trace(gc, ergo, cset)(&quot;Start choosing CSet. pending cards: &quot; SIZE_FORMAT &quot; predicted base time: %1.2fms remaining time: %1.2fms target pause time: %1.2fms&quot;,
pending_cards, base_time_ms, time_remaining_ms, target_pause_time_ms);
&#x2F;&#x2F; The young list is laid with the survivor regions from the previous
&#x2F;&#x2F; pause are appended to the RHS of the young list, i.e.
&#x2F;&#x2F; [Newly Young Regions ++ Survivors from last pause].
uint survivor_region_length &#x3D; survivors-&gt;length();
uint eden_region_length &#x3D; _g1h-&gt;eden_regions_count();
init_region_lengths(eden_region_length, survivor_region_length);
verify_young_cset_indices();
&#x2F;&#x2F; Clear the fields that point to the survivor list - they are all young now.
survivors-&gt;convert_to_eden();
_bytes_used_before &#x3D; _inc_bytes_used_before;
time_remaining_ms &#x3D; MAX2(time_remaining_ms - _inc_predicted_elapsed_time_ms, 0.0);
log_trace(gc, ergo, cset)(&quot;Add young regions to CSet. eden: %u regions, survivors: %u regions, predicted young region time: %1.2fms, target pause time: %1.2fms&quot;,
eden_region_length, survivor_region_length, _inc_predicted_elapsed_time_ms, target_pause_time_ms);
&#x2F;&#x2F; The number of recorded young regions is the incremental
&#x2F;&#x2F; collection set&#39;s current size
set_recorded_rs_lengths(_inc_recorded_rs_lengths);
double young_end_time_sec &#x3D; os::elapsedTime();
phase_times()-&gt;record_young_cset_choice_time_ms((young_end_time_sec - young_start_time_sec) * 1000.0);
return time_remaining_ms;
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>下面是老年代的部分</p><pre class="line-numbers language-C++" data-language="C++"><code class="language-C++">void G1CollectionSet::finalize_old_part(double time_remaining_ms) &#123;
double non_young_start_time_sec &#x3D; os::elapsedTime();
double predicted_old_time_ms &#x3D; 0.0;
if (collector_state()-&gt;in_mixed_phase()) &#123;
cset_chooser()-&gt;verify();
const uint min_old_cset_length &#x3D; _policy-&gt;calc_min_old_cset_length();
const uint max_old_cset_length &#x3D; _policy-&gt;calc_max_old_cset_length();
uint expensive_region_num &#x3D; 0;
bool check_time_remaining &#x3D; _policy-&gt;adaptive_young_list_length();
HeapRegion* hr &#x3D; cset_chooser()-&gt;peek();
while (hr !&#x3D; NULL) &#123;
if (old_region_length() &gt;&#x3D; max_old_cset_length) &#123;
&#x2F;&#x2F; Added maximum number of old regions to the CSet.
log_debug(gc, ergo, cset)(&quot;Finish adding old regions to CSet (old CSet region num reached max). old %u regions, max %u regions&quot;,
old_region_length(), max_old_cset_length);
break;
&#125;
&#x2F;&#x2F; Stop adding regions if the remaining reclaimable space is
&#x2F;&#x2F; not above G1HeapWastePercent.
size_t reclaimable_bytes &#x3D; cset_chooser()-&gt;remaining_reclaimable_bytes();
double reclaimable_percent &#x3D; _policy-&gt;reclaimable_bytes_percent(reclaimable_bytes);
double threshold &#x3D; (double) G1HeapWastePercent;
if (reclaimable_percent &lt;&#x3D; threshold) &#123;
&#x2F;&#x2F; We&#39;ve added enough old regions that the amount of uncollected
&#x2F;&#x2F; reclaimable space is at or below the waste threshold. Stop
&#x2F;&#x2F; adding old regions to the CSet.
log_debug(gc, ergo, cset)(&quot;Finish adding old regions to CSet (reclaimable percentage not over threshold). &quot;
&quot;old %u regions, max %u regions, reclaimable: &quot; SIZE_FORMAT &quot;B (%1.2f%%) threshold: &quot; UINTX_FORMAT &quot;%%&quot;,
old_region_length(), max_old_cset_length, reclaimable_bytes, reclaimable_percent, G1HeapWastePercent);
break;
&#125;
double predicted_time_ms &#x3D; predict_region_elapsed_time_ms(hr);
if (check_time_remaining) &#123;
if (predicted_time_ms &gt; time_remaining_ms) &#123;
&#x2F;&#x2F; Too expensive for the current CSet.
if (old_region_length() &gt;&#x3D; min_old_cset_length) &#123;
&#x2F;&#x2F; We have added the minimum number of old regions to the CSet,
&#x2F;&#x2F; we are done with this CSet.
log_debug(gc, ergo, cset)(&quot;Finish adding old regions to CSet (predicted time is too high). &quot;
&quot;predicted time: %1.2fms, remaining time: %1.2fms old %u regions, min %u regions&quot;,
predicted_time_ms, time_remaining_ms, old_region_length(), min_old_cset_length);
break;
&#125;
&#x2F;&#x2F; We&#39;ll add it anyway given that we haven&#39;t reached the
&#x2F;&#x2F; minimum number of old regions.
expensive_region_num +&#x3D; 1;
&#125;
&#125; else &#123;
if (old_region_length() &gt;&#x3D; min_old_cset_length) &#123;
&#x2F;&#x2F; In the non-auto-tuning case, we&#39;ll finish adding regions
&#x2F;&#x2F; to the CSet if we reach the minimum.
log_debug(gc, ergo, cset)(&quot;Finish adding old regions to CSet (old CSet region num reached min). old %u regions, min %u regions&quot;,
old_region_length(), min_old_cset_length);
break;
&#125;
&#125;
&#x2F;&#x2F; We will add this region to the CSet.
time_remaining_ms &#x3D; MAX2(time_remaining_ms - predicted_time_ms, 0.0);
predicted_old_time_ms +&#x3D; predicted_time_ms;
cset_chooser()-&gt;pop(); &#x2F;&#x2F; already have region via peek()
_g1h-&gt;old_set_remove(hr);
add_old_region(hr);
hr &#x3D; cset_chooser()-&gt;peek();
&#125;
if (hr &#x3D;&#x3D; NULL) &#123;
log_debug(gc, ergo, cset)(&quot;Finish adding old regions to CSet (candidate old regions not available)&quot;);
&#125;
if (expensive_region_num &gt; 0) &#123;
&#x2F;&#x2F; We print the information once here at the end, predicated on
&#x2F;&#x2F; whether we added any apparently expensive regions or not, to
&#x2F;&#x2F; avoid generating output per region.
log_debug(gc, ergo, cset)(&quot;Added expensive regions to CSet (old CSet region num not reached min).&quot;
&quot;old: %u regions, expensive: %u regions, min: %u regions, remaining time: %1.2fms&quot;,
old_region_length(), expensive_region_num, min_old_cset_length, time_remaining_ms);
&#125;
cset_chooser()-&gt;verify();
&#125;
stop_incremental_building();
log_debug(gc, ergo, cset)(&quot;Finish choosing CSet. old: %u regions, predicted old region time: %1.2fms, time remaining: %1.2f&quot;,
old_region_length(), predicted_old_time_ms, time_remaining_ms);
double non_young_end_time_sec &#x3D; os::elapsedTime();
phase_times()-&gt;record_non_young_cset_choice_time_ms((non_young_end_time_sec - non_young_start_time_sec) * 1000.0);
QuickSort::sort(_collection_set_regions, _collection_set_cur_length, compare_region_idx, true);
&#125;<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面第三行是个判断,当前是否是 mixed 回收阶段,如果不是的话其实是没有老年代什么事的,所以可以看到代码基本是从这个 if 判断<br><code>if (collector_state()-&gt;in_mixed_phase()) &#123;</code>开始往下走的<br>先写到这,偏向于做笔记用,有错轻拍</p></div><footer class="post-footer"><div class="post-eof"></div></footer></article></div><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content"><link itemprop="mainEntityOfPage" href="https://nicksxs.me/2019/09/23/AbstractQueuedSynchronizer/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/uploads/avatar.jpg"><meta itemprop="name" content="Nicksxs"><meta itemprop="description" content="learn from zero,技术博客,Nicksxs,史学森"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Nicksxs's Blog"></span><header class="post-header"><h2 class="post-title" itemprop="name headline"><a href="/2019/09/23/AbstractQueuedSynchronizer/" class="post-title-link" itemprop="url">AbstractQueuedSynchronizer</a></h2><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间:2019-09-23 23:40:05" itemprop="dateCreated datePublished" datetime="2019-09-23T23:40:05+08:00">2019-09-23</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/java/" itemprop="url" rel="index"><span itemprop="name">java</span></a> </span></span><span id="/2019/09/23/AbstractQueuedSynchronizer/" class="post-meta-item leancloud_visitors" data-flag-title="AbstractQueuedSynchronizer" title="阅读次数"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数:</span> <span class="leancloud-visitors-count"></span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">Disqus:</span> <a title="disqus" href="/2019/09/23/AbstractQueuedSynchronizer/#disqus_thread" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="2019/09/23/AbstractQueuedSynchronizer/" itemprop="commentCount"></span></a></span></div></div></header><div class="post-body" itemprop="articleBody"><p>最近看了大神的 AQS 的文章,之前总是断断续续地看一点,每次都知难而退,下次看又从头开始,昨天总算硬着头皮看完了第一部分<br>首先 AQS 只要有这些属性</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 头结点,你直接把它当做 当前持有锁的线程 可能是最好理解的</span>
<span class="token keyword">private</span> <span class="token keyword">transient</span> <span class="token keyword">volatile</span> <span class="token class-name">Node</span> head<span class="token punctuation">;</span>
<span class="token comment">// 阻塞的尾节点,每个新的节点进来,都插入到最后,也就形成了一个链表</span>
<span class="token keyword">private</span> <span class="token keyword">transient</span> <span class="token keyword">volatile</span> <span class="token class-name">Node</span> tail<span class="token punctuation">;</span>
<span class="token comment">// 这个是最重要的,代表当前锁的状态,0代表没有被占用,大于 0 代表有线程持有当前锁</span>
<span class="token comment">// 这个值可以大于 1,是因为锁可以重入,每次重入都加上 1</span>
<span class="token keyword">private</span> <span class="token keyword">volatile</span> <span class="token keyword">int</span> state<span class="token punctuation">;</span>
<span class="token comment">// 代表当前持有独占锁的线程,举个最重要的使用例子,因为锁可以重入</span>
<span class="token comment">// reentrantLock.lock()可以嵌套调用多次,所以每次用这个来判断当前线程是否已经拥有了锁</span>
<span class="token comment">// if (currentThread == getExclusiveOwnerThread()) &#123;state++&#125;</span>
<span class="token keyword">private</span> <span class="token keyword">transient</span> <span class="token class-name">Thread</span> exclusiveOwnerThread<span class="token punctuation">;</span> <span class="token comment">//继承自AbstractOwnableSynchronizer</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>大概了解了 aqs 底层的双向等待队列,<br>结构是这样的<br><img data-src="https://tva1.sinaimg.cn/large/006tNbRwly1g9mxu0ndt1j319o08w0t7.jpg"><br>每个 node 里面主要是的代码结构也比较简单</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">Node</span> <span class="token punctuation">&#123;</span>
<span class="token comment">// 标识节点当前在共享模式下</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Node</span> SHARED <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Node</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 标识节点当前在独占模式下</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Node</span> EXCLUSIVE <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token comment">// ======== 下面的几个int常量是给waitStatus用的 ===========</span>
<span class="token comment">/** waitStatus value to indicate thread has cancelled */</span>
<span class="token comment">// 代码此线程取消了争抢这个锁</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> CANCELLED <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token comment">/** waitStatus value to indicate successor's thread needs unparking */</span>
<span class="token comment">// 官方的描述是,其表示当前node的后继节点对应的线程需要被唤醒</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> SIGNAL <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span>
<span class="token comment">/** waitStatus value to indicate thread is waiting on condition */</span>
<span class="token comment">// 本文不分析condition,所以略过吧,下一篇文章会介绍这个</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> CONDITION <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">2</span><span class="token punctuation">;</span>
<span class="token comment">/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/</span>
<span class="token comment">// 同样的不分析,略过吧</span>
<span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> PROPAGATE <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">3</span><span class="token punctuation">;</span>
<span class="token comment">// =====================================================</span>
<span class="token comment">// 取值为上面的1、-1、-2、-3,或者0(以后会讲到)</span>
<span class="token comment">// 这么理解,暂时只需要知道如果这个值 大于0 代表此线程取消了等待,</span>
<span class="token comment">// ps: 半天抢不到锁,不抢了,ReentrantLock是可以指定timeouot的。。。</span>
<span class="token keyword">volatile</span> <span class="token keyword">int</span> waitStatus<span class="token punctuation">;</span>
<span class="token comment">// 前驱节点的引用</span>
<span class="token keyword">volatile</span> <span class="token class-name">Node</span> prev<span class="token punctuation">;</span>
<span class="token comment">// 后继节点的引用</span>
<span class="token keyword">volatile</span> <span class="token class-name">Node</span> next<span class="token punctuation">;</span>
<span class="token comment">// 这个就是线程本尊</span>
<span class="token keyword">volatile</span> <span class="token class-name">Thread</span> thread<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其实可以主要关注这个 <code>waitStatus</code> 因为这个是后面的节点给前面的节点设置的,等于-1 的时候代表后面有节点等待,需要去唤醒,<br>这里使用了一个变种的 CLH 队列实现,CLH 队列相关内容可以查看这篇 <a target="_blank" rel="noopener" href="https://coderbee.net/index.php/concurrent/20131115/577">自旋锁、排队自旋锁、MCS锁、CLH锁</a></p></div><footer class="post-footer"><div class="post-eof"></div></footer></article></div><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content"><link itemprop="mainEntityOfPage" href="https://nicksxs.me/2019/06/18/openresty/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/uploads/avatar.jpg"><meta itemprop="name" content="Nicksxs"><meta itemprop="description" content="learn from zero,技术博客,Nicksxs,史学森"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Nicksxs's Blog"></span><header class="post-header"><h2 class="post-title" itemprop="name headline"><a href="/2019/06/18/openresty/" class="post-title-link" itemprop="url">openresty</a></h2><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间:2019-06-18 19:03:05" itemprop="dateCreated datePublished" datetime="2019-06-18T19:03:05+08:00">2019-06-18</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/nginx/" itemprop="url" rel="index"><span itemprop="name">nginx</span></a> </span></span><span id="/2019/06/18/openresty/" class="post-meta-item leancloud_visitors" data-flag-title="openresty" title="阅读次数"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数:</span> <span class="leancloud-visitors-count"></span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">Disqus:</span> <a title="disqus" href="/2019/06/18/openresty/#disqus_thread" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="2019/06/18/openresty/" itemprop="commentCount"></span></a></span></div></div></header><div class="post-body" itemprop="articleBody"><p>目前公司要对一些新的产品功能做灰度测试,因为在后端业务代码层面添加判断比较麻烦,所以想在nginx上做点手脚,就想到了openresty<br>前后也踩了不少坑,这边先写一点</p><p>首先是日志<br><code>error_log logs/error.log debug;</code><br>需要nginx开启日志的debug才能看到日志</p><p>使用 <code>lua_code_cache off</code>即可, 另外注意只有使用 <code>content_by_lua_file</code> 才会生效</p><pre class="line-numbers language-lua" data-language="lua"><code class="language-lua"><span class="token function">http</span> <span class="token punctuation">&#123;</span>
lua_code_cache off<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
location <span class="token operator">~</span><span class="token operator">*</span> <span class="token operator">/</span><span class="token punctuation">(</span>\d<span class="token operator">+</span><span class="token operator">-</span><span class="token punctuation">.</span><span class="token operator">*</span><span class="token punctuation">)</span><span class="token operator">/</span>api<span class="token operator">/</span>orgunits<span class="token operator">/</span><span class="token function">load_all</span><span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
default_type <span class="token string">'application/json;charset=utf-8'</span><span class="token punctuation">;</span>
content_by_lua_file <span class="token operator">/</span>data<span class="token operator">/</span>projects<span class="token operator">/</span>xxx<span class="token operator">/</span>current<span class="token operator">/</span>lua<span class="token operator">/</span>controller<span class="token operator">/</span>load_data<span class="token punctuation">.</span>lua<span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用lua给nginx请求response头添加内容可以用这个</p><pre class="line-numbers language-lua" data-language="lua"><code class="language-lua">ngx<span class="token punctuation">.</span>header<span class="token punctuation">[</span><span class="token string">'response'</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">'header'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><a target="_blank" rel="noopener" href="http://cyukang.com/2017/05/22/try-on-openresty.html">使用总结</a></p><p>后续:</p><ol><li>一开始在本地环境的时候使用content_by_lua_file只关注了头,后来发到测试环境发现请求内容都没代理转发到后端服务上<br>网上查了下发现content_by_lua_file是将请求的所有内容包括response都用这里面的lua脚本生成了,content这个词就表示是请求内容<br>后来改成了access_by_lua_file就正常了,只是要去获取请求内容和修改响应头,并不是要完整的接管请求</li></ol><ol start="2"><li><p>后来又碰到了一个坑是nginx有个client_body_buffer_size的配置参数,nginx在32位和64位系统里有8K和16K两个默认值,当请求内容大于这两个值的时候,会把请求内容放到临时文件里,这个时候openresty里的ngx.req.get_post_args()就会报“failed to get post args: requesty body in temp file not supported”这个错误,将client_body_buffer_size这个参数配置调大一点就好了</p></li><li><p>还有就是lua的异常捕获,网上看一般是用pcall和xpcall来进行保护调用,因为问题主要出在cjson的decode,这里有两个解决方案,一个就是将cjson.decode使用pcall封装,</p><pre class="line-numbers language-lua" data-language="lua"><code class="language-lua"><span class="token keyword">local</span> decode <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"cjson"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>decode
<span class="token keyword">function</span> <span class="token function">json_decode</span><span class="token punctuation">(</span> str <span class="token punctuation">)</span>
<span class="token keyword">local</span> ok<span class="token punctuation">,</span> t <span class="token operator">=</span> <span class="token function">pcall</span><span class="token punctuation">(</span>decode<span class="token punctuation">,</span> str<span class="token punctuation">)</span>
<span class="token keyword">if</span> <span class="token keyword">not</span> ok <span class="token keyword">then</span>
<span class="token keyword">return</span> <span class="token keyword">nil</span>
<span class="token keyword">end</span>
<span class="token keyword">return</span> t
<span class="token keyword">end</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个是使用了pcall,称为保护调用,会在内部错误后返回两个参数,第一个是false,第二个是错误信息<br>还有一种是使用cjson.safe包</p><pre class="line-numbers language-lua" data-language="lua"><code class="language-lua"><span class="token keyword">local</span> json <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"cjson.safe"</span><span class="token punctuation">)</span>
<span class="token keyword">local</span> str <span class="token operator">=</span> <span class="token string">[[ &#123;"key:"value"&#125; ]]</span>
<span class="token keyword">local</span> t <span class="token operator">=</span> json<span class="token punctuation">.</span><span class="token function">decode</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span>
<span class="token keyword">if</span> t <span class="token keyword">then</span>
ngx<span class="token punctuation">.</span><span class="token function">say</span><span class="token punctuation">(</span><span class="token string">" --> "</span><span class="token punctuation">,</span> <span class="token function">type</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">end</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>cjson.safe包会在解析失败的时候返回nil</p></li><li><p>还有一个是redis链接时如果host使用的是域名的话会提示“failed to connect: no resolver defined to resolve “redis.xxxxxx.com””,这里需要使用nginx的resolver指令,<br><code>resolver 8.8.8.8 valid=3600s;</code></p></li><li><p>还有一点补充下<br>就是业务在使用redis的时候使用了db的特性,所以在lua访问redis的时候也需要执行db,这里lua的redis库也支持了这个特性,可以使用instance:select(config:get(‘db’))来切换db</p></li><li><p>性能优化tips<br><a target="_blank" rel="noopener" href="https://juejin.im/entry/5b0e8fcef265da09210072a3#openresty">建议是尽量少使用阶段钩子,例如content_by_lua_file,*_by_lua</a></p></li><li><p>发现一个不错的openresty站点<br><a target="_blank" rel="noopener" href="http://orhub.org/">地址</a></p></li></ol></div><footer class="post-footer"><div class="post-eof"></div></footer></article></div><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content"><link itemprop="mainEntityOfPage" href="https://nicksxs.me/2017/05/09/ambari-summary/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/uploads/avatar.jpg"><meta itemprop="name" content="Nicksxs"><meta itemprop="description" content="learn from zero,技术博客,Nicksxs,史学森"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Nicksxs's Blog"></span><header class="post-header"><h2 class="post-title" itemprop="name headline"><a href="/2017/05/09/ambari-summary/" class="post-title-link" itemprop="url">ambari-summary</a></h2><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间:2017-05-09 23:53:05" itemprop="dateCreated datePublished" datetime="2017-05-09T23:53:05+08:00">2017-05-09</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/data-analysis/" itemprop="url" rel="index"><span itemprop="name">data analysis</span></a> </span></span><span id="/2017/05/09/ambari-summary/" class="post-meta-item leancloud_visitors" data-flag-title="ambari-summary" title="阅读次数"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数:</span> <span class="leancloud-visitors-count"></span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">Disqus:</span> <a title="disqus" href="/2017/05/09/ambari-summary/#disqus_thread" itemprop="discussionUrl"><span class="post-comments-count disqus-comment-count" data-disqus-identifier="2017/05/09/ambari-summary/" itemprop="commentCount"></span></a></span></div></div></header><div class="post-body" itemprop="articleBody"><h2 id="初识ambari"><a href="#初识ambari" class="headerlink" title="初识ambari"></a>初识ambari</h2><p><a target="_blank" rel="noopener" href="http://ambari.apache.org/">ambari</a>是一个大数据平台的管理工具,包含了<code>hadoop</code>, <code>yarn</code>, <code>hive</code>, <code>hbase</code>, <code>spark</code>等大数据的基础架构和工具,简化了数据平台的搭建,之前只是在同事搭建好平台后的一些使用,这次有机会从头开始用<code>ambari</code>来搭建一个测试的数据平台,过程中也踩到不少坑,简单记录下。</p><h2 id="简单过程"><a href="#简单过程" class="headerlink" title="简单过程"></a>简单过程</h2><ul><li>第一个坑<br>在刚开始是按照官网的指南,用maven构建,因为GFW的原因,导致反复失败等待,也就是这个<a target="_blank" rel="noopener" href="https://cwiki.apache.org/confluence/display/AMBARI/Installation+Guide+for+Ambari+2.5.0">guide</a>,因为对maven不熟悉导致有些按图索骥,浪费了很多时间,之后才知道可以直接加repo用yum安装,然而用yum安装马上就出现了第二个坑。</li><li>第二个坑<br>因为在线的repo还是因为网络原因很慢很慢,用proxychains勉强把ambari-server本身安装好了,<a target="_blank" rel="noopener" href="http://public-repo-1.hortonworks.com/ambari/centos7/2.x/updates/2.5.0.3/ambari.repo">ambari.repo</a>将这个放进<code>/etc/yum.repos.d/</code>路径下,然后<code>yum update &amp;&amp; yum install ambari-server</code>安装即可,如果有条件就用proxychains走下代理。</li><li>第三步<br>安装好ambari-server后先执行<code>ambari-server setup</code>做一些初始化设置,其中包含了JDK路径的设置,数据库设置,设置好就OK了,然后执行<code>ambari-server start</code>启动服务,这里有个小插曲,因为<code>ambari-server</code>涉及到这么多服务,所以管理控制监控之类的模块是必不可少的,这部分可以在<code>ambari-server</code>的web ui界面安装,也可以命令行提前安装,这部分被称为<code>HDF Management Pack</code>,运行```ambari-server install-mpack \</li><li>-mpack=<a target="_blank" rel="noopener" href="http://public-repo-1.hortonworks.com/HDF/centos7/2.x/updates/2.1.4.0/tars/hdf_ambari_mp/hdf-ambari-mpack-2.1.4.0-5.tar.gz">http://public-repo-1.hortonworks.com/HDF/centos7/2.x/updates/2.1.4.0/tars/hdf_ambari_mp/hdf-ambari-mpack-2.1.4.0-5.tar.gz</a> \</li><li>-purge \</li><li>-verbose```<br>安装,当然这个压缩包可以下载之后指到本地路径安装,然后就可以重启<code>ambari-server</code></li></ul></div><footer class="post-footer"><div class="post-eof"></div></footer></article></div><nav class="pagination"><a class="extend prev" rel="prev" href="/page/23/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/23/">23</a><span class="page-number current">24</span><a class="page-number" href="/page/25/">25</a><span class="space">&hellip;</span><a class="page-number" href="/page/29/">29</a><a class="extend next" rel="next" href="/page/25/"><i class="fa fa-angle-right" aria-label="下一页"></i></a></nav></div></main><footer class="footer"><div class="footer-inner"><div class="copyright">&copy; <span itemprop="copyrightYear">2022</span> <span class="with-love"><i class="fa fa-heart"></i> </span><span class="author" itemprop="copyrightHolder">Nicksxs</span></div><div class="busuanzi-count"><span class="post-meta-item" id="busuanzi_container_site_uv"><span class="post-meta-item-icon"><i class="fa fa-user"></i> </span><span class="site-uv" title="总访客量"><span id="busuanzi_value_site_uv"></span> </span></span><span class="post-meta-item" id="busuanzi_container_site_pv"><span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="site-pv" title="总访问量"><span id="busuanzi_value_site_pv"></span></span></span></div></div></footer><script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js" integrity="sha256-yt2kYMy0w8AbtF89WXb2P1rfjcP/HTHLT7097U8Y5b8=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/lozad@1.16.0/dist/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/comments.min.js"></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/utils.min.js"></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/next-boot.min.js"></script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/search/local-search.min.js"></script><script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script class="next-config" data-name="leancloud_visitors" type="application/json">{"enable":true,"app_id":"ysza182Vghlqjdt7QiwGLLJy-gzGzoHsz","app_key":"s9GDqbn7gnGGkusf66YRVccw","server_url":"https://leancloud.cn","security":true}</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/statistics/lean-analytics.min.js"></script><script src="https://cdn.jsdelivr.net/npm/quicklink@2.2.0/dist/quicklink.umd.js" integrity="sha256-4kQf9z5ntdQrzsBC3YSHnEz02Z9C1UeW/E9OgnvlzSY=" crossorigin="anonymous"></script><script class="next-config" data-name="quicklink" type="application/json">{"enable":false,"home":false,"archive":false,"delay":true,"timeout":3000,"priority":true,"url":"https://nicksxs.me/page/24/"}</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/quicklink.min.js"></script><script class="next-config" data-name="disqus" type="application/json">{"enable":true,"shortname":"nicksxs","count":true,"i18n":{"disqus":"disqus"}}</script><script src="https://cdn.jsdelivr.net/npm/hexo-theme-next@8.6.1/source/js/third-party/comments/disqus.min.js"></script></body></html>