<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>e-una</title>
    <link>https://e-una.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 3 Aug 2026 12:04:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>e-una</managingEditor>
    <image>
      <title>e-una</title>
      <url>https://tistory1.daumcdn.net/tistory/3823682/attach/6a3ceca7c07044c8a437bacc815b6a8e</url>
      <link>https://e-una.tistory.com</link>
    </image>
    <item>
      <title>WebFlux 적용하기 (1) Java I/O 와 NIO(New I/O, Non-Blocking I/O)</title>
      <link>https://e-una.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 프로젝트를 WebFlux 로 Async-Non Blocking 하게 만들어보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebFlux 는 Reactive Programming 을 지원하는 프레임워크로, 디폴트로 Netty 라는 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;NIO 클라이언트 서버 프레임워크를 사용합니다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Webflux 적용하기 첫 번째 포스팅으로&lt;/span&gt;&amp;nbsp;&lt;code&gt;Java I/O 와 NIO&lt;/code&gt;&amp;nbsp;를 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;느린 Java 의 I/O&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바에 New I/O 가 추가 된 이유는 무엇일까요? 그 이유는 기존 I/O 의 속도가 느리기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 의 I/O 가 어떤 이유로 느린지, 그리고 NIO 는 어떤 점을 개선했는지 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java I/O 의 동작 원리(파일 읽기)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUp0ic/btrYy9x4dQw/7EXHbdGkP8VbnQZmdi1gak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUp0ic/btrYy9x4dQw/7EXHbdGkP8VbnQZmdi1gak/img.png&quot; data-alt=&quot;[하드디스크 -&amp;amp;gt; 프로세스 내부의 메모리 영역] 논리적 다이어그램&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUp0ic/btrYy9x4dQw/7EXHbdGkP8VbnQZmdi1gak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUp0ic%2FbtrYy9x4dQw%2F7EXHbdGkP8VbnQZmdi1gak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;227&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[하드디스크 -&amp;gt; 프로세스 내부의 메모리 영역] 논리적 다이어그램&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로세스는 커널에 read() 시스템 콜을 통해 버퍼를 채우도록 요청합니다.&lt;/li&gt;
&lt;li&gt;read() 가 호출되면 커널은 디스크에서 데이터를 가져오도록 디스크 컨트롤러에 명령을 내립니다.&lt;/li&gt;
&lt;li&gt;디스크 컨트롤러는 메인 CPU 의 지원 없이 DMA(Direct Memory Access) 를 통해 커널의 버퍼에 직접 데이터를 기록합니다.&lt;/li&gt;
&lt;li&gt;디스크 컨트롤러가 버퍼를 채우면 &lt;b&gt;&lt;u&gt;커널은 커널의 임시 버퍼에서 프로세스가 지정한 버퍼로 데이터를 복사&lt;/u&gt;&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;⤹ 간략한 OS 개념 정리&lt;/span&gt;&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OS 는 크게 User mode, Kernel mode 두 가지로 구분해서 프로세스를 동작시킵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간략하게 설명하면 Kernel 은 OS 의 핵심적인 역할을 하기 때문에 User 레벨에서 직접 접근할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 애플리케이션(프로세스)에서 하드웨어에 접근해서 데이터를 읽고 싶을 때는 어떻게 해야할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;System call&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 사용됩니다.&amp;nbsp;시스템 콜은 사용자 레벨 프로세스가 OS와 상호작용하는 방법으로, 인터페이스를 통해 애플리케이션에게 OS 의 서비스를 요청할 수 있도록 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read() 시스템 콜을 통해 커널에게 I/O 수행을 요청하면 User mode 에서 Kernal mode 로 스위칭이 발생합니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &amp;nbsp;JVM 버퍼 복사로 인한 CPU 오버헤드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C언어에서는 포인터를 통해 메모리에 직접 접근할 수 있기 때문에&amp;nbsp;시스템 콜을 직접 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C 프로세스 ⇨&amp;nbsp;시스템 콜 ⇨&amp;nbsp;커널 ⇨&amp;nbsp;디스크 컨트롤러 ⇨&amp;nbsp;데이터 복사&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Java 는 메모리에 직접 접근할 수 없기 때문에&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;커널 버퍼에서 &lt;b&gt;JVM 버퍼로 데이터를 복사하는 추가적인 과정&lt;/b&gt;이 발생합니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JVM ⇨ 시스템 콜 ⇨&amp;nbsp;커널 ⇨&amp;nbsp;디스크 컨트롤러 ⇨&amp;nbsp;커널 버퍼 복사 ⇨&amp;nbsp;JVM 버퍼 복사&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크 컨트롤러는 DMA 를 통해 CPU 연산 없이 데이터를 복사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 &lt;b&gt;JVM 내부의 버퍼로 복사할 때는 CPU 연산이 필요하기 때문에 속도가 느려지는 원인&lt;/b&gt;이 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  Blocking 방식의 I/O&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;354&quot;&gt;&lt;a href=&quot;https://developer.ibm.com/articles/l-async/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baWr0b/btrYz9qUzbh/Mytajvb2am9QXCIGrYBio1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaWr0b%2FbtrYz9qUzbh%2FMytajvb2am9QXCIGrYBio1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;354&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read() 시스템 콜을 통해 커널에게 I/O 수행을 요청하면 User mode 에서 Kernel mode 로 스위칭이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크에서부터 JVM 내 메모리 영역에 복사하는 과정을 거친 후에 User mode 로 제어권을 넘기기 때문에, 데이터를 읽는 동안에 &lt;b&gt;Blocking 이 발생하여 속도가 느려지는 원인&lt;/b&gt;이 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하나의 스레드에 하나의 클라이언트가 붙는 Blocking 방식은 다음과 같은 단점이 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;여러 스레드가 입력이나 출력 데이터가 들어오기를 기다리며 무한정 대기 상태로 유지 될 수 있습니다.&lt;/li&gt;
&lt;li&gt;각 스레드가 스택 메모리를 할당해야하기 때문에 많은 메모리를 차지합니다.&lt;/li&gt;
&lt;li&gt;JVM 은 동시 접속이 한계에 이르기 훨씬 전부터(약 1만개만 되어도) 컨텍스트 스위칭에 따른 오버헤드가 문제가 될 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Java NIO&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java NIO 는 &lt;b&gt;표준 Java I/O 및 Java Networking API 의 대안이 될 수 있는 API&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 I/O 와 다른 방식의 프로그래밍 모델을 제공하고, Blocking 또는 &lt;u&gt;&lt;b&gt;Non-Blocking 방식&lt;/b&gt;&lt;/u&gt; 모두 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIO 는 내용이 &lt;b&gt;&lt;i&gt;매우 매우&lt;/i&gt;&lt;/b&gt; 많기 때문에, 중요한 내용을 위주로 기재하도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NIO Components&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java NIO 의 주요한 컴포넌트에는 &lt;b&gt;채널, 버퍼, 셀렉터&lt;/b&gt;가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 컴포넌트에 대해서 먼저 간략하게 알아보고, 동작 방식을 통해 각 컴포넌트들이 어떻게 쓰이는지 이해해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NIO Channels&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단방향 데이터 전송 방식인 스트림과 다르게 채널은 양방향 데이터 전송 기능을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널은 데이터를 버퍼에서 읽고, 버퍼는 데이터를 채널에 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3cAkx/btrYYo8D0tR/dUyaEKfbAYFLjQQVZgDU30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3cAkx/btrYYo8D0tR/dUyaEKfbAYFLjQQVZgDU30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3cAkx/btrYYo8D0tR/dUyaEKfbAYFLjQQVZgDU30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3cAkx%2FbtrYYo8D0tR%2FdUyaEKfbAYFLjQQVZgDU30%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;180&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널의 타입은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FileChannel - 파일에서 데이터 읽기&lt;/li&gt;
&lt;li&gt;DatagramChannel - UDP 를 통해 네트워크에서 데이터 읽고 쓰기&lt;/li&gt;
&lt;li&gt;SocketChannel - TCP 를 통해 네트워크에서 데이터 읽고 쓰기&lt;/li&gt;
&lt;li&gt;ServerSocketChannel - 웹 서버와 같이 들어오는 TCP 연결을 수신, 들어오는 각 연결에 대해 SocketChannel 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널은 &lt;u&gt;&lt;b&gt;네이티브 코드를 사용해서 작업을 수행하기 때문에 I/O 서비스에 직접 액세스&lt;/b&gt;&lt;/u&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I/O 매커니즘에 액세스하기 위해 Java NIO 에서 제공하는 게이트웨이 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널은 데이터를 읽고 쓰는데 &lt;u&gt;&lt;b&gt;Scatter/gather (분산/수집) 방식&lt;/b&gt;&lt;/u&gt;을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Scattering 이란, 둘 이상의 버퍼에서 단일 채널로 데이터를 쓰는 쓰기 작업을 의미합니다. 여러 버퍼를 하나의 채널로 수집하려고 할 때, 매버퍼마다 비용이 비싼 시스템 콜을 호출하지 않고 프로세스에서 사용하는 버퍼의 목록을 한 번에 넘겨주는 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NIO Buffers&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIO 버퍼는 NIO 채널과 상호 작용할 때 사용됩니다. 앞서 말한 것처럼 데이터는 채널에서 버퍼로 읽혀지고, 버퍼에서 채널로 쓰여집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼는 다음과 같은 유형을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;331&quot;&gt;&lt;a href=&quot;https://www.javatpoint.com/java-nio-buffers&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mQCGx/btrYG8z4k99/5adhvohT0ZNEfSRK0dXMd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmQCGx%2FbtrYG8z4k99%2F5adhvohT0ZNEfSRK0dXMd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;331&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼는 사용하는 메모리의 위치에 따라서 Non Direct Buffer 와 Direct Buffer 로 나뉩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;272&quot;&gt;&lt;a href=&quot;https://palpit.tistory.com/641&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bC0kgh/btrYI6nFtJ5/HqAoH4QssE3KCgSKU2k6zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbC0kgh%2FbtrYI6nFtJ5%2FHqAoH4QssE3KCgSKU2k6zk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;272&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;채널을 통해 다이렉트 버퍼(e.g. ByteBuffer)를 사용하면 OS 의 메모리에 직접 접근(하는 듯)&lt;/b&gt;&lt;/u&gt;하여 다음과 같이 파일을 읽을 수 있게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JVM ⇨ 시스템 콜&amp;nbsp;⇨ 커널 ⇨&amp;nbsp;디스크 컨트롤러 ⇨&amp;nbsp;DMA 복사&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크 컨트롤러가 DMA 를 통해 직접 운영체제 메모리에 접근하고, 응용 프로그램은 다이렉트 버퍼를 사용해서 운영체제 메모리에 직접 접근합니다. 이 때 논 다이렉트 버퍼를 사용하게 되면 채널의 대상이 되지 않아 기존 I/O 와 같이 데이터 복사의 단계를 한 번 더 거치게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼를 사용하는 간단한 예제는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1676368992435&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;RandomAccessFile aFile = new RandomAccessFile(&quot;data/nio-data.txt&quot;, &quot;rw&quot;);
FileChannel inChannel = aFile.getChannel();

//create buffer with capacity of 48 bytes
ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf); //read into buffer.
while (bytesRead != -1) {

  buf.flip();  //make buffer ready for read

  while(buf.hasRemaining()){
      System.out.print((char) buf.get()); // read 1 byte at a time
  }

  buf.clear(); //make buffer ready for writing
  bytesRead = inChannel.read(buf);
}
aFile.close();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;NIO Selector&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀렉터는 단일 스레드를 사용해서 여러 채널을 처리하는 데 사용됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 스레드로 여러 I/O 를 처리할 수 있기 때문에,&amp;nbsp;이전에 설명한 블로킹 방식의 단점들이 해결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;246&quot;&gt;&lt;a href=&quot;https://www.javatpoint.com/java-nio-selector&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLe7NN/btrYUSIJvTL/apRSYAYGHbBSRflC0YEXx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLe7NN%2FbtrYUSIJvTL%2FapRSYAYGHbBSRflC0YEXx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;246&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;Non Blocking 구현의 핵심&lt;/b&gt;&lt;/u&gt;으로,&amp;nbsp;등록 된 채널들이 발생시킨 이벤트에 대해서 요청을 매핑하는 컨트롤러 역할을 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;347&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/kslisenko/networking-in-java-with-nio-and-netty-76583794&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5n1FW/btrYSC1FPzq/mb1xZKX6ARAku526HqaXt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5n1FW%2FbtrYSC1FPzq%2Fmb1xZKX6ARAku526HqaXt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;347&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Selector 생성 및 채널 등록&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;채널을 셀렉터와 사용하기 위해서는 채널을 셀렉터에 등록해야합니다. 소스코드로 보면 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1676297650964&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Creating a Selector
Selector selector = Selector.open();

// Registering Channels with the Selector
channel.configureBlocking(false);

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;SelectionKey&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셀렉터에 채널을 등록할 때마다 셀렉션 키가 생성됩니다. 셀렉션 키는&amp;nbsp;셀렉터를 통해 모니터링되는 &lt;b&gt;채널에서 수신하려는 이벤트를 의미&lt;/b&gt;하는 키입니다.&amp;nbsp;셀렉션 키를 기준으로 채널들이 발생시킨 이벤트에 대해 적절한 핸들러로 요청을 분기시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수신할 수 있는 이벤트는 4가지가 있으며, 각각은 SelectionKey 클래스의 상수로 표시됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트를 발생시키는 채널은 해당 이벤트에 대해 ready 되었다고 표현합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SelectionKey.OP_CONNECT: 클라이언트가 서버에 연결을 시도할 때&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_ACCEPT: 서버가 클라이언트의 연결을 수락할 때&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_READ: 데이터를 read 할 ready 된 채널&lt;/li&gt;
&lt;li&gt;SelectionKey.OP_WRITE: 데이터를 write 할 ready 된 채널&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ready&amp;nbsp;된 채널에 다음과 같이 액세스 할 수 있습니다.&amp;nbsp;전체 코드는 &lt;a href=&quot;https://www.baeldung.com/java-nio-selector&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;를 참고 해주세요.&lt;/p&gt;
&lt;pre id=&quot;code_1676300480079&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Server 에서 사용 예시
while (true) {
    selector.select();
    Set&amp;lt;SelectionKey&amp;gt; selectedKeys = selector.selectedKeys();
    Iterator&amp;lt;SelectionKey&amp;gt; iter = selectedKeys.iterator();
    while (iter.hasNext()) {

        SelectionKey key = iter.next();

        if (key.isAcceptable()) {
            register(selector, serverSocket);
        }

        if (key.isReadable()) {
            answerWithEcho(buffer, key);
        }
        iter.remove();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NIO Server Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIO 서버가 어떻게 데이터를 주고받는지 동작 순서를 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0NHkX/btrZkSByvPB/UYBhpogZtsTiE02hwvkKD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0NHkX/btrZkSByvPB/UYBhpogZtsTiE02hwvkKD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0NHkX/btrZkSByvPB/UYBhpogZtsTiE02hwvkKD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0NHkX%2FbtrZkSByvPB%2FUYBhpogZtsTiE02hwvkKD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1990&quot; height=&quot;958&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;ready 된 채널들이 셀렉터에 등록됩니다.&lt;/li&gt;
&lt;li&gt;셀렉터는 단일 스레드지만 동시에 여러 이벤트를 처리할 수 있는 멀티 스레드처럼 동작합니다.&lt;/li&gt;
&lt;li&gt;클라이언트에게 요청이 오면 셀렉터는 셀렉션 키를 보고 적절한 채널을 찾아 매핑합니다.&lt;/li&gt;
&lt;li&gt;채널은 버퍼를 통해 클라이언트가 요청한 이벤트를 수행합니다.&lt;/li&gt;
&lt;li&gt;새로운 커넥션이 생성되면 셀렉터에 등록하고, 동일한 처리를 반복합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;NIO&amp;nbsp;&lt;/b&gt;Virtual Memory&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java I/O 가 느린 이유 중 하나는 메모리에 직접 접근할 수 없어서 JVM 내부에 데이터를 한번 더 복사해야한다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 점을 NIO 에서는 가상 메모리를 통해 개선했습니다.&amp;nbsp;가상 메모리를 사용해서 커널 버퍼와 프로세스의 버퍼가 같은 물리적 메모리 공간을 바라보게하면, 마치&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;프로세스 내의 버퍼가 커널 버퍼에 직접 접근하는 것처럼 동작&lt;/b&gt;이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;383&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UJy2K/btrYJRwBS5Q/6BPvvbpOZsmFnRaRkpqfU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UJy2K/btrYJRwBS5Q/6BPvvbpOZsmFnRaRkpqfU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UJy2K/btrYJRwBS5Q/6BPvvbpOZsmFnRaRkpqfU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUJy2K%2FbtrYJRwBS5Q%2F6BPvvbpOZsmFnRaRkpqfU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;383&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;383&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Netty 에 대해 알아보기 전에 기존 I/O 의 문제점과 NIO 를 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NIO 는 다소 복잡하지만 가상 메모리를 사용함으로써 컨텍스트 스위칭 비용이 적고 Non Blocking 방식이 가능하다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 커넥션되는 클라이언트 수가 적고 대용량의 데이터를 전송하는 경우에는 기존 I/O 방식이 더 유리할 수 있기 때문에 용도에 맞게 선택해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅은 Netty 에 대해서 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://howtodoinjava.com/java/io/how-java-io-works-internally/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;How Java IO Works Internally?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@maketheworldwise/IO가-느린-이유가-뭘까-NIO는-뭘까&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;IO가 느린 이유가 뭘까? NIO는 뭘까?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;a href=&quot;https://brewagebear.github.io/fundamental-nio-and-io-models/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바 NIO의 동작원리 및 IO 모델&lt;/a&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://palpit.tistory.com/entry/Java-NIO-기반-입출력-및-네트워킹-버퍼Buffer&quot;&gt;[Java] NIO 기반 입출력 및 네트워킹 - 버퍼(Buffer)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.slideshare.net/kslisenko/networking-in-java-with-nio-and-netty-76583794&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Networking in Java with NIO and Netty&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Boot/WebFlux</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/76</guid>
      <comments>https://e-una.tistory.com/76#entry76comment</comments>
      <pubDate>Tue, 14 Feb 2023 19:10:40 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Tomcat 알아보기 (3) Tomcat Thread Pool</title>
      <link>https://e-una.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 어떻게 동시에 수많은 요청을 처리할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서는 Servlet Container 와 Java Thread Pool 에 대해 알아보았습니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;/73&quot;&gt;[Spring Boot] Tomcat 알아보기 (1) Servlet 과 Servlet Container&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;/74&quot;&gt;[Spring Boot] Tomcat 알아보기 (2) Java Thread Pool&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 동시 요청을 처리하는 &lt;code&gt;Tomcat 의 Thread Pool&lt;/code&gt; 에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Tomcat 의 Thread Pool&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서 알아본 것처럼, &lt;b&gt;&lt;u&gt;Spring 에서 클라이언트의 요청은 Tomcat 과 같은 Servlet Container 가 처리&lt;/u&gt;&lt;/b&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat 의 Thread Pool 의 구조는 Java Thread Pool 과 유사하기 때문에 설정 방법과 효율적인 사용법에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣은 Spring Boot 의 내장 Servlet Container 중 하나로, &lt;code&gt;application.yml&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;또는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;application.properties&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;와 같은 애플리케이션 속성 파일로 Tomcat 의 Thread Pool 을 설정할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Properties&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1674135473737&quot; class=&quot;yaml&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server:
  tomcat:
    threads:
      max: 200
      min-spare: 10
    max-connections: 8192
    accept-count: 100 # Task queue size
    connection-timeout: 20000&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- server.tomcat.threads.max&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread Pool 에서 사용할 최대 스레드 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request 에 비해 스레드 개수를 너무 많게 설정하면, idle 스레드가 많아져 비효율이 발생하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 너무 적게 설정하면 동시에 처리할 수 있는 요청 수가 줄어들어 평균 응답 시간이 늘어나기 때문에 적절한 설정이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- server.tomcat.threads.min-spare&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread Pool 에서 최소한으로 유지할 Thread 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 값을 많이 설정하게 된다면 Thread Pool 이 항상 유지해야 할 Thread 수가 많아지기 때문에 메모리를 많이 차지하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- server.tomcat.accept-count&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요청 처리 스레드가 사용 중일 때 들어오는 커넥션 request 에 대한 최대 대기열 길이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 작업 큐 사이즈를 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 크게 설정하면 대기열이 커지면서 메모리 문제를 유발할 수 있고, 너무 작게 설정하면 요청이 몰렸을 때 장애를 발생시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정을 통해 부적절하거나 잘못된 요청이 한 번에 너무 많이 들어와서 서버에 장애를 발생시키는 것을 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- server.tomcat.threads.max-connections&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣이 동시에 처리할 수 있는 최대 Connection 의 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;378&quot;&gt;&lt;a href=&quot;https://www.adservio.fr/post/tomcat-performance-best-practices&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/deGDug/btrXBN3cwsI/4ibLjyKCKrsHG00jDtiLk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdeGDug%2FbtrXBN3cwsI%2F4ibLjyKCKrsHG00jDtiLk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1352&quot; height=&quot;378&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;톰캣은 accept-count&amp;nbsp;속성을 기반으로 연결을 수락할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &lt;code&gt;max-connections(최대 연결 수) 가 3, max(최대 스레드 개수) 가 2, accept-count(대기열) 가 1&lt;/code&gt; 이라고 가정해보겠습니다. 이런 경우&amp;nbsp;최대 스레드 개수가 2개지만 작업 큐에 커넥션(Task)을 1개 넣을 수 있으므로, 연결을 수락할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat5 이후 지원하는 NIO 커넥터는 BIO 커넥터에 비해 스레드를 효율적으로 사용하지만, 두 방식 모두 대기열 큐가 가득차게 되면 연결 거절 오류를 리턴할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Thread Pool 을 설정하는 이유와 한계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 알아본 것과 같이 Thread Pool 의 세부 사항을 설정하는 이유는 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유는 여러가지가 있겠지만, 그 중 하나는 한정 된 서버 자원 내에서 클라이언트의 요청을 효율적으로 처리해주기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버실이나 IDC 에 비싼 장비를 사두고 그 자원 내에서 최대한 많은 클라이언트의 요청을 받아야했을 때는 세부 설정이 중요했지만&amp;nbsp;현재 많은 기업은 AWS 를 통해 서비스하고 있습니다. 그렇기 때문에 서버의 리소스가 부족하다면 스레드를 줄이고 늘리며 요청을 처리하는 것보다 더 안정적인 AWS 의 인스턴스를 늘리는 방향으로도 문제를 해결할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 Thread Pool 모델에는 더 근본적인 한계가 있습니다. Request&amp;nbsp;마다 하나의 Thread 를 할당해주는 Thread per request 라는 점입니다. 전세계 트래픽의 15% 를 차지한다고 알려진 넷플릭스와 같은 엄청난 서비스들이 등장하면서 하나의 요청에 하나의 스레드를 할당하는 전통적인 Spring MVC 모델은 한계를 가지게 되었습니다. 이러한 문제로 인해 요즘은 Reactive Progamming, Spring webflux 를 많이 사용한다고 하는데요. 다음 포스팅은 webflux 에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/um4rYmQIeRE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[10분 테코톡] 조조그린의 Thread Pool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Common Application Properties&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.adservio.fr/post/tomcat-performance-best-practices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tomcat Performance Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring Boot</category>
      <category>ThreadPool</category>
      <category>Tomcat</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/75</guid>
      <comments>https://e-una.tistory.com/75#entry75comment</comments>
      <pubDate>Mon, 30 Jan 2023 16:09:48 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Tomcat 알아보기 (2) Java Thread Pool</title>
      <link>https://e-una.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 어떻게 동시에 수많은 요청을 처리할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서는 동시 요청 처리에 대해 알아보기 전에 Tomcat 의 Servlet Container 역할을 살펴보았습니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;/73&quot;&gt;[Spring Boot] Tomcat 알아보기 - (1) Servlet 과 Servlet Container&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 Thread Pool 의 개념과 Tomcat 에서 유사하게 사용되는 Java 의 Thread Pool 클래스에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Tomcat 알아보기&lt;/code&gt; 두 번째 포스팅 Java Thread Pool 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Thread Pool&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 에서 유저 수준 스레드는 커널 수준 스레드에 매핑됩니다. &lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고) &lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://e-una.tistory.com/70&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Java] JVM 알아보기 - (6) Thread, Java Thread Model&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 제어할 수 없이 스레드를 많이 생성하면 리소스가 빠르게 고갈될 수 있습니다. 또한&amp;nbsp;스레드가 늘어날수록 컨텍스트 스위칭 비용도 늘어나게 됩니다. 이러한 문제를 해결하기 위해 Thread Pool 패턴이 도입되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Thread Pool 의&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;동작 과정은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;370&quot;&gt;&lt;a href=&quot;https://www.baeldung.com/thread-pool-java-and-guava&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0PxfU/btrWwJt8HMi/KUsD3qhxlH0060S6k2twd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0PxfU%2FbtrWwJt8HMi%2FKUsD3qhxlH0060S6k2twd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;370&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Thread pool 에 미리 설정한 사이즈 만큼의 스레드를 생성합니다.&lt;/li&gt;
&lt;li&gt;Task(Connection, 소켓 객체 등) 처리 요청이 오면&amp;nbsp;Task Queue 에 Task 를 넣어둡니다.&lt;/li&gt;
&lt;li&gt;idle 상태인 스레드가 있다면, Task 큐에서 Task 를 꺼내 해당 스레드에 할당합니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 idle 상태인 스레드가 없다면, 작업은 Task Queue 에서 대기합니다.&lt;/li&gt;
&lt;li&gt;그 상태가 지속되어 Task Queue 가 꽉 차면 스레드를 새로 생성합니다.&lt;/li&gt;
&lt;li&gt;이 과정을 반복하다가 스레드 최대 사이즈에 도달하고 작업큐도 꽉 차게 되면, 추가 요청에 대해서 connection-refused 오류를 리턴합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Task 가 완료되면 스레드는 다시 idle 상태로 돌아갑니다.
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Task Queue 가 비어있고 core size 이상의 스레드가 생성되어 있다면 스레드를 destory 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드 풀은&lt;b&gt; 일정량의 스레드를 미리 만들어두고 Task Queue 를 이용해 Task 를 처리하는 패턴&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드를 미리 만들어두고 사용하기 때문에 새로운 스레드를 생성하는 비용을 줄일 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 스레드의 개수를 제한하기 때문에 CPU 오버헤드를 방지할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 스레드 풀을 사용하면 &lt;u&gt;여러 개의 작업을 동시에 안정적으로 처리&lt;/u&gt;할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Java 의 Thread Pool&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat 은 Java 기반이기 때문에 Java 의 Thread Pool 클래스와 매우 유사한 Thread Pool 구현체를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 의 Thread Pool 사용법에 대해 간략하게 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;기본 용어 정리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;corePoolSize &amp;amp; maximumPoolSize&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 task 가 submit 될 때 스레드 풀에서 corePoolSize 미만의 스레드가 실행 중인 경우, 다른 스레드가 idle 상태인 경우에도 요청을 처리하기 위해 새 스레드가 생성됩니다. 실행 중인 스레드가 corePoolSize&amp;nbsp;보다는 크고 maximumPoolSize 보다 작은 경우, task 큐가 가득 찬 경우에만 새 스레드가 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;keepAliveTime&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀에 현재 corePoolSize 보다 많은 스레드가 있는 경우, keepAliveTime 보다 더 오래 idle 상태인 경우 초과 스레드가 종료됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Executors, Executor 및 ExecutorService&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복잡한 애플리케이션에서 직접 Thread 를 만들고 관리하는 것은 매우 어려운 일이기 때문에&amp;nbsp;Java5 는 스레드 관리를 추상화하는 &lt;code&gt;Executor&lt;/code&gt; 를 도입했습니다. Executor 는 개발자 대신 스레드의 라이프 사이클, 사용 및 스케줄링 등을 관리합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Executor 의 상속 관계는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;298&quot;&gt;&lt;a href=&quot;http://geekrai.blogspot.com/2013/07/executor-framework-in-java.html&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c1NxmU/btrWHneWtTA/D8N0X1pCSuMBVzDHMKuX90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc1NxmU%2FbtrWHneWtTA%2FD8N0X1pCSuMBVzDHMKuX90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;482&quot; height=&quot;298&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Executors&lt;/code&gt;&amp;nbsp;라는 유틸 클래스는 Execute, ExecutorService, ScheduledExecutorService 등의 인스턴스 또는 스레드 풀을 생성하는 여러 메서드를 제공합니다. 사용자가 세부 설정을 할 필요가 없는 경우 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Executor&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Executor 인터페이스에는 Runnable 인스턴스를 submit 하는 단일 실행 메서드가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Task 를 처리하는 단일 작업자 스레드로, 어떠한 이유로 인해 스레드가 죽으면 교체합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 10개의 작업이 있는 대기열이 있다면, 작업은 순서에 따라 차례대로 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 간단하게 &quot;Hello World&quot; 를 출력하는 단일 작업을 실행하는 예제입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1674123595852&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Executor executor = Executors.newSingleThreadExecutor();
executor.execute(() -&amp;gt; System.out.println(&quot;Hello World&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ExecutorService&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ExecutorService 에는 Task 진행을 컨트롤하고 서비스의 종료를 관리하는 여러 메서드가 포함되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 인터페이스를 사용하여 실행할 작업을 제출하고 반환된 Future 인스턴스를 사용하여 실행을 제어할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 executorService 를 사용해서 task 를 submit 하고, task 의 리턴 값을 get() 으로 받아올 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1674122506462&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ExecutorService executorService = Executors.newFixedThreadPool(10);
Future&amp;lt;String&amp;gt; future = executorService.submit(() -&amp;gt; &quot;Hello World&quot;);
// some operations
String result = future.get();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ThreadPoolExecutor&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부 설정이 가능한, 많은 매개 변수가 있는 스레드풀 구현 클래스입니다. 가장 일반적인 구성은 Executors 에 정의되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Executors.newFixedThreadPool()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674123425854&quot; class=&quot;haxe&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue&amp;lt;Runnable&amp;gt;());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 타입의 풀에는 항상 지정된 수의 스레드가 실행됩니다.&amp;nbsp;풀의 스레드가 종료되면, 자동으로 새 스레드로 대체됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 실행되는 task 의 수가 지정한 스레드 수보다 작거나 같으면 즉시 실행되지만, 그렇지 않으면 대기열에 놓이게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Executors.newCachedThreadPool()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674123425855&quot; class=&quot;haxe&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue&amp;lt;Runnable&amp;gt;());
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 가능한 경우 이전에 생성된 스레드를 재사용하는 스레드 풀을 생성합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;corePoolSize 를 0 으로 지정하고, maximumPoolSize 를 Interger 최대 값으로 생성합니다.&amp;nbsp;keepAliveTime 은 60초 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 매개변수 값은 캐시된 스레드 풀이 submit 된 task 를 수용하기 위해서 제한 없이 커질 수 있음을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 스레드가 60초 동안 활동이 없으면 destroy 되기 때문에, 애플리케이션에 수명이 짧은 작업이 많은 경우 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;ScheduledThreadPoolExecutor&lt;/b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일정한 딜레이 후에 실행하거나 주기적으로 실행할 명령을 예약할 수 있는 클래스입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;schedule()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674134836132&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public ScheduledFuture&amp;lt;?&amp;gt; schedule(Runnable command,long delay, TimeUnit unit);

public &amp;lt;V&amp;gt; ScheduledFuture&amp;lt;V&amp;gt; schedule(Callable&amp;lt;V&amp;gt; callable, long delay, TimeUnit unit);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정된 delay&amp;nbsp;후에 작업을 한 번 실행할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;scheduleAtFixedRate()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674134795692&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public ScheduledFuture&amp;lt;?&amp;gt; scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정된 initialDelay 후에 작업을 실행하고, 일정 기간 동안 주기적으로 실행할 수 있습니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;period 매개 변수는 연속 실행 사이의 기간을 나타냅니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;scheduleWithFixedDelay()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1674134903175&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public ScheduledFuture&amp;lt;?&amp;gt; scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 작업을 반복적으로 실행한다는 점에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;scheduleAtFixedRate 와 유사하지만, initialDelay 후에 작업을 시작하고 실행이 종료 된 후에 delay 만큼의 지연이 발생합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 선수 지식으로 Thread Pool 의 동작 과정과 구현 클래스들에 대해서 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅은 드디어 Tomcat 의 Thread Pool 을 알아볼 수 있게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/thread-pool-java-and-guava&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung, Introduction to Thread Pools in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://geekrai.blogspot.com/2013/07/executor-framework-in-java.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Executor Framework in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sihyung92.oopy.io/spring/1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스프링부트는 어떻게 다중 유저 요청을 처리할까? (Tomcat9.0 Thread Pool)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=um4rYmQIeRE&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[10분 테코톡] 조조그린의 Thread Pool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Boot</category>
      <category>java</category>
      <category>Thread Pool</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/74</guid>
      <comments>https://e-una.tistory.com/74#entry74comment</comments>
      <pubDate>Thu, 19 Jan 2023 22:47:36 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Tomcat 알아보기 (1) Servlet 과 Servlet Container</title>
      <link>https://e-una.tistory.com/73</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트는 어떻게 동시에 수많은 요청을 처리할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Tomcat 알아보기&lt;/code&gt;&amp;nbsp;첫 번째 포스팅으로 Servlet 과 Servlet Container 에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Servlet&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.oracle.com/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/javax/servlet/Servlet.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바 docs&lt;/a&gt; 에서 정의하는 서블릿은 다음과 같습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.&lt;br /&gt;&lt;br /&gt;서블릿은 웹 서버 내에서 실행되는 작은 자바 프로그램입니다. 서블릿은 일반적으로 HTTP 를 통해 웹 클라이언트의 요청을 수신하고 응답합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 웹 페이지를 사용할 때 서버로부터 정적인 리소스를 받기도 하지만, 대부분은 우리가 입력하는 데이터에 따라 변경되는 동적인 리소스를 응답 받습니다. 서블릿을 사용하면 사용자로부터 입력을 받고, 그 데이터에 맞는 웹페이지를 동적으로 생성할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'그냥 스프링의 컨트롤러 아냐?' 라고 생각할 수도 있지만,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 우리가 구현하는 컨트롤러는 HTTP 를 통해 client 와 request/response 를 주고받지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 기능은 스프링의 DispatcherServlet 이 하고 있죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;172&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FxuN9/btrWghsLgLr/HefOXYLC9QRISbmQ3PdI9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFxuN9%2FbtrWghsLgLr%2FHefOXYLC9QRISbmQ3PdI9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;172&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DispatcherServlet 클래스의 상속 관계를 보면, Servlet API 의 구현체인 HttpServlet 을 확인 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링은 사용자의 요청이 오면 DispatcherServlet(Front Controller) 라는 서블릿을 통해 요청을 받고 적합한 컨트롤러를 찾아 위임해주기 때문에,&amp;nbsp;개발자들은 컨트롤러에서 간편하게 클라이언트의 요청을 처리할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;317&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-framework/docs/3.0.0.M4/spring-framework-reference/html/ch15s02.html&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6RQlG/btrWqHjra8k/NtLkc2jaKozlUROMmZtNm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6RQlG%2FbtrWqHjra8k%2FNtLkc2jaKozlUROMmZtNm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;592&quot; height=&quot;376&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 인터페이스는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1673881115720&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package javax.servlet;

import java.io.IOException;

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();

    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;init(), service(), destory() 메서드를 확인하고 이어서 서블릿의 라이프 사이클에 대해 살펴보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Servlet Container 와 LifeCycle&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 자바 코드에서 new 키워드를 사용하면 JVM 이 객체의 인스턴스를 생성합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 서블릿 클래스의 인스턴스를 생성하고 관리하는 것은 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그것 또한 별도의 JVM 위에서 동작하는 자바 프로그램입니다. 이를 &lt;b&gt;Servlet Container&lt;/b&gt; 라고 부릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Servlet Container 를 포함하고 있는 것이 바로 &lt;b&gt;Tomcat, Jetty 등의&amp;nbsp;&lt;u&gt;WAS(Web Application Server)&lt;/u&gt;&lt;/b&gt; 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat 에서 동작하는 서블릿의 LifeCycle 을 알아봅시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;277&quot;&gt;&lt;a href=&quot;https://codeburst.io/understanding-java-servlet-architecture-b74f5ea64bf4&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3LgjK/btrWsUioJXK/1KmbY6wn7CaWgBwKz6sDGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3LgjK%2FbtrWsUioJXK%2F1KmbY6wn7CaWgBwKz6sDGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;600&quot; height=&quot;277&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;414&quot;&gt;&lt;a href=&quot;https://velog.io/@jihoson94/Servlet-Container-정리&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A4mr1/btrWr5RCtsR/D629FB9muZyTF0J2xGzxxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA4mr1%2FbtrWr5RCtsR%2FD629FB9muZyTF0J2xGzxxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;414&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Client 가 request 를 합니다.&lt;/li&gt;
&lt;li&gt;톰캣이 가지고 있는 Connector 중 하나를 통해서 request 를 전달받습니다.&lt;/li&gt;
&lt;li&gt;톰캣은 해당 요청을 처리하기 위해 적절한 Engine 을 거쳐 적절한 Servlet 에 매핑합니다.&lt;/li&gt;
&lt;li&gt;요청이 서블릿에 매핑되면, 톰캣은 힙 메모리에 해당 서블릿의 인스턴스가 존재하는지 확인합니다. 존재하지 않는다면, 인스턴스를 생성하고 인스턴스의 &lt;b&gt;&lt;u&gt;init()&lt;/u&gt; 메서드를 호출하여 인스턴스를 초기화&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li&gt;서블릿이 초기화되면 톰캣은 여러 스레드를 생성하여 여러 요청을 처리합니다.&lt;/li&gt;
&lt;li&gt;톰캣은 각 스레드의 단일 서블릿 인스턴스의&amp;nbsp;&lt;b&gt;&lt;u&gt;service()&lt;/u&gt; 메서드를 호출해서 HTTP 요청을 처리&lt;/b&gt;합니다. HTTP request method 에 따라 doGet, doPost 등의 메서드를 적절하게 호출합니다.&lt;/li&gt;
&lt;li&gt;서블릿이 살아있는 동안, 다양한 상태 변화를 모니터링할 수 있는 listener 클래스를 사용할 수 있습니다. 톰캣은 다양한 방식으로 상태 변화를 저장 및 조회할 수 있고 다른 서블릿들도 해당 데이터에 접근할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용하지 않는 서블릿은 &lt;u&gt;destroy()&lt;/u&gt; 메서드를 호출&lt;/b&gt;하여 DB Connection 을 닫고, GC 의 대상이 되도록 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Servlet 의 필요성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Servlet 의 필요성을 두 가지로 정리해 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Java 스레드를 통한 요청 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿이 생기기 이전에는 CGI(Common Gateway Interface) 를 사용해서 외부와 데이터를 주고받았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CGI 는 C 또는 C++ 로 CGI 프로그램을 만들어서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;request 마다 프로세스를 생성&lt;/u&gt;하여 동적인 컨텐츠를 만드는 방법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 요청마다 프로세스를 생성했기 때문에 클라이언트 수가 증가할수록 많은 비용이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;181&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTpDJ6/btrWrqhNYnu/2BAU6iWCnJlrLm42YYQT21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTpDJ6/btrWrqhNYnu/2BAU6iWCnJlrLm42YYQT21/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTpDJ6/btrWrqhNYnu/2BAU6iWCnJlrLm42YYQT21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTpDJ6%2FbtrWrqhNYnu%2F2BAU6iWCnJlrLm42YYQT21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;181&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;181&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 CGI 의 단점을 극복하기 위해 Java 기반의 서블릿이 등장했습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;200&quot;&gt;&lt;a href=&quot;https://www.javatpoint.com/servlet-tutorial&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zNWu4/btrWq3NVK5E/cCyHCQ9gaDze7mGnlckLIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzNWu4%2FbtrWq3NVK5E%2FcCyHCQ9gaDze7mGnlckLIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;200&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;200&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿의 장점은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스가 아닌 각 요청에 대해 스레드를 생성하기 때문에 비용이 낮습니다.&lt;/li&gt;
&lt;li&gt;JVM 에 의해 관리되기 때문에 메모리 릭, GC 등을 걱정하지 않아도 됩니다.&lt;/li&gt;
&lt;li&gt;Java EE Platform 의 표준으로 동일한 컨테이너와 동일한 서블릿을 사용하기 때문에 확장성이 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;SRP(Single Responsibility Principle) 원칙을 따른 설계&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;452&quot;&gt;&lt;a href=&quot;https://velog.io/@jakeseo_me/자바-서블릿에-대해-알아보자.-근데-톰캣과-스프링을-살짝-곁들인#서블릿과-cgi의-결정적-차이는&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRtqW0/btrWv8mW3kq/72aIxogUQKZq7d4zVrm8eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRtqW0%2FbtrWv8mW3kq%2F72aIxogUQKZq7d4zVrm8eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;800&quot; height=&quot;452&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;452&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서블릿 컨테이너에서 Socket Connection, Thread 등의 관리를 하기 때문에&amp;nbsp;서블릿은 제공하고자 하는 서비스에만 집중할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 서블릿은 서블릿 컨테이너의 책임에 대한 의존성이 없어서, 다른 환경에서도 쉽게 재사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Thread Pool 의 등장&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tomcat 3.2 이전 버전에서는 유저의 요청이 들어올 때 마다 Thread 를 하나씩 생성하고, 사용이 끝나면 destory 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 방식은 다음과 같은 단점이 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 요청에 대해 스레드를 생성하고 소멸하는 것은 OS 와 JVM 에게 필요없는 부하를 일으킵니다.&lt;/li&gt;
&lt;li&gt;동시에 다수의 요청이 들어올 경우 CPU 또는 메모리 리소스 소모에 대한 제한이 어렵습니다. 결국 순간적으로 서버가 다운되거나 동시 처리에 문제가 생길 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에 대한 해결책으로 Tomcat 3.2 부터는 디폴트로 Thread Pool 을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 Tomcat Thread Pool 에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스팅 내 이미지를 클릭하면 출처로 이동합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/intro-to-servlets&quot;&gt;Baeldung, Introduction to Java Servlets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javatpoint.com/servlet-tutorial&quot;&gt;javaTpoint, Servlets | Servlet Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codeburst.io/understanding-java-servlet-architecture-b74f5ea64bf4&quot;&gt;Understanding Java Servlet Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@sihyung92/how-does-springboot-handle-multiple-requests&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;스프링부트는 어떻게 다중 유저 요청을 처리할까? (Tomcat9.0 Thread Pool)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@jakeseo_me/자바-서블릿에-대해-알아보자.-근데-톰캣과-스프링을-살짝-곁들인&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;자바 서블릿에 대해 알아보자. 근데 톰캣과 스프링을 살짝 곁들인&lt;/a&gt;&lt;/li&gt;
&lt;li id=&quot;6dd2&quot;&gt;&lt;a href=&quot;https://sigridjin.medium.com/servletcontainer와-springcontainer는-무엇이-다른가-626d27a80fe5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ServletContainer와 SpringContainer는 무엇이 다른가?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@jihoson94/Servlet-Container-정리&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Servlet Container 정리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://wiki.kldp.org/KoreanDoc/html/Tomcat-KLDP/Tomcat-KLDP-12.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;12. Thread Pool 지원 (Tomcat3.2.1)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring Boot</category>
      <category>Servlet</category>
      <category>ServletContainer</category>
      <category>Tomcat</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/73</guid>
      <comments>https://e-una.tistory.com/73#entry73comment</comments>
      <pubDate>Tue, 17 Jan 2023 13:46:20 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] DTO 는 왜, 언제 사용할까?</title>
      <link>https://e-una.tistory.com/72</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Spring Boot 프로젝트를 시작할 때마다 고민의 대상이 되는 &lt;code&gt;DTO&lt;/code&gt; 에 대해 알아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 DTO 의 필요성에 대해 깊이 고민하지 않고 사용해서 그 기준을 잡기가 어려웠는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅을 통해 DTO 의 책임과 역할을 이해하고 저만의 사용 가이드라인을 만들어보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;1. DTO(Data Transfer Object)&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;DTO&amp;nbsp;&lt;b&gt;(Data Transfer Object)&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, DTO 의 정의를 간략하게 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO는 프로세스 간에 데이터를 전달하는 용도의 객체입니다.&amp;nbsp;비즈니스 로직을 포함하지 않는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;&lt;b&gt;데이터를 전달하기 위한 단순한 객체&lt;/b&gt;&lt;/u&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;를 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9FaUJ/btrUavkZBFF/eur8vEaMzaz9Y7gDFua3d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9FaUJ/btrUavkZBFF/eur8vEaMzaz9Y7gDFua3d1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9FaUJ/btrUavkZBFF/eur8vEaMzaz9Y7gDFua3d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9FaUJ%2FbtrUavkZBFF%2Feur8vEaMzaz9Y7gDFua3d1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;151&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MVC 패턴에서는 주로 Client 와 Controller 사이에서 DTO 가 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 보통 Controller 에서는 DTO 로 데이터를 전달받고, 애플리케이션 내부에서는 Domain(또는 Entity 또는 Model) 을 통해 데이터를 전달합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 Controller, Service, Repository, DataBase 는 서로 &lt;code&gt;User&lt;/code&gt; 라는 도메인 객체를 통해 데이터를 전달하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller 에서 Client 로 리턴할 때는 &lt;code&gt;UserDTO&lt;/code&gt; 에 매핑하여 리턴하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드로 구현하면 아래와 같습니다.&amp;nbsp;코드는 baeldung 의 예제를 가져왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DTO 를 통해 원하는 정보만 보여주기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671546592515&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class User {

    private String id;
    private String name;
    private String email;
    private String password;

    public User(String name, String email, String password) {
        this.name = Objects.requireNonNull(name);
        this.email = Objects.requireNonNull(email);
        this.password = this.encrypt(password);
    }

    // Getters and Setters

   String encrypt(String password) {
       // encryption logic
   }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;User 객체는&amp;nbsp;id, name, email, password 의 값을 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;password 라는 공개할 수 없는 값을 가지고 있기 때문에 User 객체 그대로 Client 에게 전달할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 Client 에게 노출해도 되는 필드로만 이루어진 UserDTO 가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671546654022&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class UserDTO {
    private String name;
    private String email;
    
    // standard getters and setters
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Service, Repository 에서는 User 객체로 데이터를 전달하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller 에서 DTO 객체로 매핑해서 Client 에게 리턴합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671546847524&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@RestController
@RequestMapping(&quot;/users&quot;)
class UserController {

    private UserService userService;
    private Mapper mapper;

    // Constructor

    @GetMapping
    @ResponseBody
    public List&amp;lt;UserDTO&amp;gt; getUsers() {
        return userService.getAll()
          .stream()
          .map(mapper::toDto)
          .collect(toList());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1671548263602&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
class Mapper {
    public UserDTO toDto(User user) {
        String name = user.getName();
        String email = user.getEmail();

        return new UserDTO(name, email);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 DTO 를 사용하면 Client 에게&amp;nbsp;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;password 와 같은 값을 노출하지 않고 원하는 값만 리턴&lt;/u&gt;&lt;/span&gt;&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 막상 DTO 를 사용하려고하면 이런 의문들이 생깁니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그렇다면 클라이언트에게 노출해도 되는 값만 가진 도메인 객체라면 그대로 리턴해도 되는걸까?&lt;/li&gt;
&lt;li&gt;매번 Controller 에서 도메인 객체를 DTO 객체로 변환하는 비용을 치르더라도 DTO 를 사용해야만 할까?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 두 가지 의문에 대한 답을 찾아보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DTO 와 Domain 을 분리하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;i&gt;그렇다면 클라이언트에게 노출해도 되는 값만 가진 도메인 객체라면 그대로 리턴해도 되는걸까?&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관심사의 분리(&lt;span style=&quot;background-color: #ffffff; color: #4a4a4a;&quot;&gt;Separation of Concerns, SoC)&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 Spring Boot 와 같은 프레임 워크에서 MVC 패턴을 사용하는 이유는 무엇일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델1 방식을 사용해보신 분들은 아시겠지만, Model-Controller-View 가 각자의 역할을 수행하여 유지보수가 편리하기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Controller 는 중간 다리의 역할로 client 와 request/response 하는 책임이 있고, Model 은 DataBase 에서 받아온 데이터를 다루는 책임이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 사는 세상에서도 복잡하고 큰 기업일수록 팀이 철저하게 구분되고 다른 팀이 무엇을 하는지조차 모르는 경우가 있는 것처럼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OOP 세상에서는 &lt;b&gt;관심사의 분리&lt;/b&gt;를 통해 복잡한 시스템을 효율적으로 작동하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;73&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfo3G2/btrVjNxVrFT/RoofJxoDiUmLAZYkvT7br0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfo3G2/btrVjNxVrFT/RoofJxoDiUmLAZYkvT7br0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfo3G2/btrVjNxVrFT/RoofJxoDiUmLAZYkvT7br0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfo3G2%2FbtrVjNxVrFT%2FRoofJxoDiUmLAZYkvT7br0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;240&quot; height=&quot;73&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;73&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 '관심사' 라는 말이 잘 와닿지 않습니다.&amp;nbsp;네이버 영어사전에 concern 을 검색해보았더니 &quot;영향을 미치다&quot; 라는 의미가 나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 concerns 를 분리한다는 것은 &lt;b&gt;&quot;애플리케이션에 같은 영향을 미치는 코드들끼리 분리한다&quot;&lt;/b&gt;는 것이라고 이해할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;관심사의 수평적 분리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;336&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wlL8R/btrVkeotWCu/mth3EJiMlBjYv5gR7tSrK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wlL8R/btrVkeotWCu/mth3EJiMlBjYv5gR7tSrK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wlL8R/btrVkeotWCu/mth3EJiMlBjYv5gR7tSrK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwlL8R%2FbtrVkeotWCu%2Fmth3EJiMlBjYv5gR7tSrK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;338&quot; height=&quot;284&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;336&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관심사의 수평적 분리는 애플리케이션 내에서 동일한 역할을 수행하는 논리적 계층으로 애플리케이션을 나눈 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로세스에 따라 MVC 패턴을 녹이면 다음 그림과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFCvj5/btrUa2ivGTL/5Dt8ubKjvZVzC3AREukOfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFCvj5/btrUa2ivGTL/5Dt8ubKjvZVzC3AREukOfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFCvj5/btrUa2ivGTL/5Dt8ubKjvZVzC3AREukOfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFCvj5%2FbtrUa2ivGTL%2F5Dt8ubKjvZVzC3AREukOfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;228&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Presentation Layer: 애플리케이션의 기능과 데이터를 사용자에게 제공&lt;/li&gt;
&lt;li&gt;Business Layer: 애플리케이션의 핵심 비즈니스 로직 및 나머지 두 계층 간에 전달되는 데이터 처리&lt;/li&gt;
&lt;li&gt;Data Access Layer: 데이터베이스와 상호 작용하는 역할&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 멀리 돌아왔는데요.. DTO 와 Domain 을 분리해야하는 이유가 바로 여기에서 나옵니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO 는 오직 &lt;u&gt;데이터를 전달하는 목적&lt;/u&gt;으로 Presentation Layer 에 속합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Domain 은 &lt;u&gt;비즈니스 로직을 담는&lt;/u&gt;&amp;nbsp;Business Layer 에서 역할을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 객체를 분리하지 않는다면 Presentation, Business 가 혼합된 god class 가 탄생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;IT 대기업에서 영업과 개발을 함께하는 그저 god team 을 만들어 버리는 거랄까요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 god class 를 만든다면 변경이 있을 때 두 레이어에 영향을 미치게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 객체지향 설계를 위해서는 하나의 객체에는 하나의 책임만 존재해야 합니다.&amp;nbsp;변경이 있을 때 파급 효과를 최소한으로 하기 위해서죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마틴 파울러의 DTO 패턴&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;매번 Controller 에서 도메인 객체를 DTO 객체로 변환하는 비용을 치르더라도 DTO 를 사용해야만 할까?&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마틴 파울러는 저서 &lt;i&gt;Patterns of Enterprise Application Architecture(P of EAA)&lt;/i&gt; 에서 DTO 를 소개하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DTO 의&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;주요 목적에 대해 아래와 같이 말했습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;i&gt;&lt;span style=&quot;background-color: #ffffff; color: #303633;&quot;&gt;The main reason for using a Data Transfer Object is to batch up what would be multiple remote calls into a single call&lt;/span&gt;&lt;/i&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마틴 파울러가 말하는 DTO 의 주요 목적은 &lt;b&gt;&lt;u&gt;한 번의 호출로 여러 매개 변수를 일괄 처리해서 서버의 왕복을 줄이는 것&lt;/u&gt;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 호출에 관련 된 모든 데이터를 가지고 있는 DTO 객체를 만들어서 네트워크 비용을 줄인다는 의미입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OKKY 에 이해하기 좋은 댓글이 있어서 첨부하도록 하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;대표적으로 유저 테이블이 있다고 가정하면, 이 유저라는 정보는 광범위하게 사용될 것 입니다.&lt;br /&gt;또 게시판이 있을 수 있고, 상품 판매 화면이 있을 수 있고, 결제가 있을 수 있고, Q&amp;amp;A&amp;nbsp; 게시판이 있을 수 있습니다.&lt;br /&gt;&lt;br /&gt;그렇다면 예를 들어, 상품정보와 Q&amp;amp;A는 서로 다른 테이블에 구현이 되겠는데&lt;br /&gt;특정 상품 소개 페이지 안에 해당 상품과 관련은 없어도 연관성은 있는&lt;br /&gt;질문들의 Q&amp;amp;A 만 뽑아서 보여주고 싶다면? 거기에 구매한 몇몇 유저들의 정보까지 포함시키고 싶다면?&lt;br /&gt;&lt;br /&gt;상품소개 API&amp;nbsp; 1개&lt;br /&gt;Q&amp;amp;A&amp;nbsp; API 1개&lt;br /&gt;유저 API 1개&lt;br /&gt;&lt;br /&gt;이렇게 3개의 API를 호출하실건가요? 네 이것도 틀린 방법은 아닙니다. 잘못된 방법도 아닙니다.&lt;br /&gt;충분히 유효한 방법입니다.&lt;br /&gt;&lt;br /&gt;그런데 그렇다면 모든 불필요한 정보까지 전부 반환이 될 것이고&lt;br /&gt;&lt;br /&gt;결국 통신도 3회 이루어졌는데, 이걸 1회로 줄일 수 있다면?&lt;br /&gt;&lt;br /&gt;방법은 간단하겠죠. 서비스 3개는 존재할테고, 이를 부르는 컨트롤러 하나에서&lt;br /&gt;DTO를 준비하고, 여기에 필요한 필드를 미리 구성해놓은 다음에&lt;br /&gt;서비스 3개에서 리턴된 오브젝트에서 뽑아서 매핑시켜주면 됩니다.&lt;br /&gt;&lt;br /&gt;그러면 컨트롤러 1개가 불필요한 정보 없이 API 1개만으로 원하는 응답을 모두 해줬습니다.&lt;br /&gt;&lt;br /&gt;출처: [OKKY] 카카오콘 님, https://okky.kr/articles/1293573&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 인프런에 김영한 님이 답변하신 글도 참고하면 좋을 것 같아 첨부하겠습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;항상 유지보수에서 가장 문제가 되는 것이 바로 애매한 것입니다. &lt;br /&gt;특히 같은 필드인데, 어떤 경우에는 null이고 어떤 경우에는 값이 있고 이렇게 모호하면 정말 유지보수가 어려워집니다.&lt;br /&gt;그래서 API 응답 스펙이 정해지면 그 필드에 값은 항상 같은 원칙으로 반환되도록 명확하게 설계하는 것이 중요합니다.&lt;br /&gt;클래스를 여러개 만들더라도, 코드가 중복되는 것 처럼 보일지라도, 명확한 것이 훨씬 더 나은 선택이라는 것이지요.&lt;br /&gt;&lt;br /&gt;다만 API를 제공할 때 또 모든 케이스에 대응해서 만들면 API 자체가 너무 많아집니다. &lt;br /&gt;그래서 null 값 대신에 실제 값을 채워서 반환하는 API를 제공하는 것이 좋습니다.&lt;br /&gt;예를 들어서 username, age 둘다 제공하는 공통 API 하나를 여러곳에서 사용하도록 제공하는 것이지요.&lt;br /&gt;실무에서 API를 설계할 때 진짜 고민은, 생각보다 너무 복잡하다는 것입니다.&lt;br /&gt;&lt;br /&gt;어떻게 보면 제공 단위를 크게 만들어서 모든 데이터를 다 반환하는 API 하나를 만들면 될 것 같지만, 이렇게 너무 공통화해도 유지보수가 어렵고, 성능 이슈가 있습니다. 반대로 너무 각각의 케이스를 대응하도록 만들어도 API 자체가 많아져서 유지보수가 어렵습니다.&lt;br /&gt;이 사이에서 적절한 단위로 API를 설계하고 제공하는 것이 묘미이지요^^!&lt;br /&gt;&lt;br /&gt;제가 선호하는 방법은 기본 공통 API를 제공하고, 이 기본 공통 API로 해결이 안되는 특수한 경우에 한해서 별도의 API를 제공하는 방법을 선호합니다.&lt;br /&gt;&lt;br /&gt;출처: [인프런] 김영한 님, https://www.inflearn.com/questions/72423/dto&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 의견을 종합해 본 저의 결론은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DTO 를 사용하는 주요 목적은 한 번의 호출로 해당 호출에 관련 된 모든 데이터를 담은 객체를 리턴 받아 사용하는 것입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;네트워크 비용 &amp;gt; 컨트롤러에서 매번 변환하는 비용&lt;/code&gt; 이라고 판단한 것입니다.&lt;/li&gt;
&lt;li&gt;이 때, 같은 필드인데 어떤 경우에는 null 이고 어떤 경우에는 값이 있다면 유지보수가 어려워집니다.&lt;/li&gt;
&lt;li&gt;그렇기 때문에 null 없이 &lt;u&gt;모든 필드에 값이 있는 공통 DTO 를 사용&lt;/u&gt;합니다.&lt;/li&gt;
&lt;li&gt;공통 DTO 가 커져서 성능에 이슈가 생기거나, 특수한 경우에만 별도의 DTO 를 사용합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 제가 DTO 에 대해 가지고 있던 의문을 다뤄보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 DTO 를 공통으로 사용하는 것에 대한 의문이 조금 있지만, 사용하다가 개선하게 되면 업데이트 하도록 하겠습니다. :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;역할, 책임, 협력 - 조영호,&amp;nbsp;객체지향의 사실과 오해, 위키북스, p.114-115&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://umbum.dev/1206&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MVC Layering Architecture: DTO 와 Domain Model 을 분리해야 하는 이유&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frogand.tistory.com/152&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[Spring] 엔티티(Entity) 또는 도메인 객체(Domain Object)와 DTO를 분리해야 하는 이유&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@eddy_song/mvc&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MVC 창시자가 말하는, MVC의 본질&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://velog.io/@boo105/Entitiy-와-DTO-의-분리&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Entitiy 와 DTO 의 분리&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aspiringcraftsman.com/2008/01/03/art-of-separation-of-concerns/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Art of Separation of Concerns&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring Boot</category>
      <category>DTO</category>
      <category>mvc</category>
      <category>관심사의분리</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/72</guid>
      <comments>https://e-una.tistory.com/72#entry72comment</comments>
      <pubDate>Tue, 3 Jan 2023 23:19:51 +0900</pubDate>
    </item>
    <item>
      <title>[Spring Boot] Maven Multi Module</title>
      <link>https://e-una.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요. 이번 포스팅에서는 메이븐으로 멀티 모듈 프로젝트를 구성하는 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;Maven&lt;/code&gt;과 &lt;code&gt;Multi Module&lt;/code&gt;이 무엇인지 알아보고, 그 후에 인텔리제이 IDE 를 사용하여 프로젝트를 구성해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;&lt;b&gt;Apache Maven&lt;/b&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 소스 코드를 작성하고 코드를 실행하기 위해 JAR 나 WAR 등의 파일을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 소스 코드 파일 및 리소스를 컴퓨터에서 실행 가능한 산출물(Artifact)로 만드는 일련의 과정을 Build 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드는 전처리, 컴파일, 패키징, 테스팅, 배포 등의 여러 과정으로 이루어지지만&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자들은 Maven 과 같은 Build Tool 을 사용하여 한 번의 클릭으로 실행 파일을 얻을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Project Object Model (POM)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메이븐은 POM 을 통해 프로젝트를 설명하고 종속성을 관리한다. 또한 멀티 모듈 프로젝트의 모듈 간 관계를 정의한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;POM 은 루트 디렉토리에 pom.xml 파일로 생성된다. 일반적인 pom.xml 의 구조를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671114824434&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;project&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.una&amp;lt;/groupId&amp;gt; // 프로젝트를 생성한 회사 또는 그룹의 고유한 기본 이름
    &amp;lt;artifactId&amp;gt;una&amp;lt;/artifactId&amp;gt; // 프로젝트의 고유한 이름
    &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt; // 패키징 방법 (e.g. war, jar, zip ...)
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;name&amp;gt;com.una&amp;lt;/name&amp;gt;
    
    &amp;lt;properties&amp;gt; // 사용자가 지정하는 속성
    	&amp;lt;spring.version&amp;gt;5.3.16&amp;lt;/spring.version&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;dependencies&amp;gt; // 프로젝트에서 사용하는 라이브러리 관리
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;junit-jupiter-api&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.8.2&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
        &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
    &amp;lt;build&amp;gt; 
        &amp;lt;plugins&amp;gt; // 빌드에 사용하는 플러그인 관리
            &amp;lt;plugin&amp;gt;
            //...
            &amp;lt;/plugin&amp;gt;
        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;groupId: 프로젝트를 생성한 회사 또는 그룹의 고유한 기본 이름&lt;/li&gt;
&lt;li&gt;artifactId: 프로젝트의 고유한 이름&lt;/li&gt;
&lt;li&gt;packaging: 패키징 방법 (e.g. war, jar, zip ...)&lt;/li&gt;
&lt;li&gt;properties: pom.xml 파일 내에서 ${name} 표기법을 사용해서 변수처럼 사용할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버전을 변경할 때 properties 내에 있는 숫자만 변경하면 해당 속성을 사용하는 모든 종속성이 업데이트되기 때문에 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dependency: 프로젝트와 의존 관계에 있는 라이브러리 관리 (종속성 관리)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Maven 은 중앙 리포지토리에서 해당 라이브러리를 자동으로 다운로드하기 때문에 로컬에 저장하지 않아도 된다.&lt;/li&gt;
&lt;li&gt;외부 라이브러리에 대한 종속성을 선언하기 위해서는 groupId, artifactId, version 을 명시해야한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Maven 의 장점과 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트의 종속성을 파일 하나로 관리할 수 있다.&lt;/li&gt;
&lt;li&gt;중앙 리포지토리에서 라이브러리를 다운로드하기 때문에 스토리지 사용량이 적다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메이븐을 통해 라이브러리를 다운받기 위해서는 메이븐을 설치해야한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Maven Wrapper 를 통해 메이븐을 설치하지 않고 사용하는 방법이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Multi Module&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈은 독립적으로 운영될 수 있는 구성요소의 단위다. 예로는 우리가 pom.xml 에 명시하는 외부 라이브러리들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 하나의 프로젝트를 멀티 모듈로 구성하는 이유가 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유에는 여러 가지가 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FrontEnd, BackEnd 를 한 번에 빌드하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 프로젝트에서 동일한 도메인을 사용하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 프로젝트에서 동일한 버전의 라이브러리를 사용하기 위해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 멀티 모듈을 구성하면 하나의 비즈니스를 여러 프로젝트로 &lt;b&gt;역할과 책임을 나누어 관리하기 용이&lt;/b&gt;해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;실습 프로젝트 구성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GM8hY/btrTLaBAqkG/nL8faErH2ULm4txA4yL9k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GM8hY/btrTLaBAqkG/nL8faErH2ULm4txA4yL9k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GM8hY/btrTLaBAqkG/nL8faErH2ULm4txA4yL9k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGM8hY%2FbtrTLaBAqkG%2FnL8faErH2ULm4txA4yL9k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;442&quot; height=&quot;327&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서는 &lt;b&gt;종속성을 루트 프로젝트에서 관리하기 위해 멀티 모듈을 구성&lt;/b&gt;해보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 프로젝트는 모든 프로젝트에서 공통적으로 사용하는 라이브러리들을 관리하는 용도로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 루트 프로젝트를 maven 으로 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daZ9eL/btrTN0rfsLq/lUzL3DkLvRyTkOKbHwMPJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daZ9eL/btrTN0rfsLq/lUzL3DkLvRyTkOKbHwMPJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daZ9eL/btrTN0rfsLq/lUzL3DkLvRyTkOKbHwMPJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaZ9eL%2FbtrTN0rfsLq%2FlUzL3DkLvRyTkOKbHwMPJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;460&quot; data-origin-width=&quot;795&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 루트 프로젝트가 생성되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;443&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tP0dd/btrTKbabHXg/TnWMl39eAM0VPYIq8wXqNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tP0dd/btrTKbabHXg/TnWMl39eAM0VPYIq8wXqNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tP0dd/btrTKbabHXg/TnWMl39eAM0VPYIq8wXqNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtP0dd%2FbtrTKbabHXg%2FTnWMl39eAM0VPYIq8wXqNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;443&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;443&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 프로젝트는 pom 의 역할을 하기 때문에 패키징을 pom 으로 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 spring-boot-starter-parent 를 선언한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671120512559&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;
    &amp;lt;packaging&amp;gt;pom&amp;lt;/packaging&amp;gt;

    &amp;lt;parent&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;2.7.2&amp;lt;/version&amp;gt;
    &amp;lt;/parent&amp;gt;

    &amp;lt;groupId&amp;gt;org.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;parent-project&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;/project&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring-boot-starter-parent 는 부모인 spring-boot-dependencies 를 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 부트를 사용하면서 필요한 여러 dependency 들이 선언되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnBTHa/btrVh5FJOfq/k8drjJv6kvYdeb6MZYyyx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnBTHa/btrVh5FJOfq/k8drjJv6kvYdeb6MZYyyx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnBTHa/btrVh5FJOfq/k8drjJv6kvYdeb6MZYyyx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnBTHa%2FbtrVh5FJOfq%2Fk8drjJv6kvYdeb6MZYyyx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;421&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;spring-boot-starter-parent 를 사용하는 이유는 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 직접&amp;nbsp;dependency 를 하나하나 추가하지 않아도 된다.&lt;/li&gt;
&lt;li&gt;사용하는 스프링 버전에 맞는&amp;nbsp;dependency 들의 버전 관리가 편리하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Spring Boot 자식 모듈을 생성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dependency 는 Spring Web 만 추가해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GRMt7/btrTMhfU98x/bE03WU8pRNDLbIcz8FBiYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GRMt7/btrTMhfU98x/bE03WU8pRNDLbIcz8FBiYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GRMt7/btrTMhfU98x/bE03WU8pRNDLbIcz8FBiYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGRMt7%2FbtrTMhfU98x%2FbE03WU8pRNDLbIcz8FBiYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;577&quot; height=&quot;144&quot; data-origin-width=&quot;577&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;666&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kKtVS/btrTMhmG7KW/1JltLoaVLqDZM7QTX9sKKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kKtVS/btrTMhmG7KW/1JltLoaVLqDZM7QTX9sKKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kKtVS/btrTMhmG7KW/1JltLoaVLqDZM7QTX9sKKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkKtVS%2FbtrTMhmG7KW%2F1JltLoaVLqDZM7QTX9sKKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;589&quot; height=&quot;539&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;666&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일하게 하나의 자식 모듈을 더 생성했다. 이제 프로젝트 구조는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parent-project 밑에 core, service 모듈을 생성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPSBsa/btrTKjzi5EA/P12JGmBN6VkQ0tzrsOoLWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPSBsa/btrTKjzi5EA/P12JGmBN6VkQ0tzrsOoLWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPSBsa/btrTKjzi5EA/P12JGmBN6VkQ0tzrsOoLWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPSBsa%2FbtrTKjzi5EA%2FP12JGmBN6VkQ0tzrsOoLWk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;376&quot; height=&quot;308&quot; data-origin-width=&quot;376&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 프로젝트의 pom.xml 에 자식 모듈을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1671120772338&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;modules&amp;gt;
    &amp;lt;module&amp;gt;core&amp;lt;/module&amp;gt;
    &amp;lt;module&amp;gt;service&amp;lt;/module&amp;gt;
&amp;lt;/modules&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 모듈의 pom.xml 을 보면 parent 로 spring-boot-starter-parent 를 바라보고 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!-- 변경 전 --&amp;gt;
&amp;lt;parent&amp;gt;
    &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;spring-boot-starter-parent&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.0.0&amp;lt;/version&amp;gt;
    &amp;lt;relativePath/&amp;gt; &amp;lt;!-- lookup parent from repository --&amp;gt;
&amp;lt;/parent&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 변경하여 루트 프로젝트를 바라보도록 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1671121022558&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;parent&amp;gt;
    &amp;lt;groupId&amp;gt;org.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;parent-project&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
&amp;lt;/parent&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 자식 모듈에서 종속성을 관리해보자. pom.xml 에 라이브러리를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자식 모듈에서 다른 버전을 사용해야 한다면 버전을 명시하여 재정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1671121313104&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-web&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-test&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널을 열어서 세 개의 모듈을 한 번에 빌드해보자. 루트 프로젝트 디렉토리에서 다음 명령어를 입력한다.&lt;/p&gt;
&lt;pre id=&quot;code_1671121665093&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mvn package&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 모듈이 한 번에 빌드 된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwYNmt/btrTLFagurj/FtHKPNMocszKVp4JPfuoak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwYNmt/btrTLFagurj/FtHKPNMocszKVp4JPfuoak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwYNmt/btrTLFagurj/FtHKPNMocszKVp4JPfuoak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwYNmt%2FbtrTLFagurj%2FFtHKPNMocszKVp4JPfuoak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;152&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 메이븐을 통해 멀티 모듈을 구성해보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 복잡한 멀티 모듈 프로젝트에는 Gradle 을 사용하면 최적화할 수 있으나&amp;nbsp;아직까지는 루트 프로젝트가 종속성 관리의 역할만 하고 있기 때문에, 그리고 이후 문제 발생 시 빌드 툴의 변경이 어렵지 않다고 하여 사용이 익숙한 Maven 으로 구성하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;5. Reference&quot; data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/maven-multi-module&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung, Multi-Module Project with Maven&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://batory.tistory.com/461&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Maven 멀티 모듈 구성방법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://techblog.woowahan.com/2637/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;우아한형제들 기술블로그,&amp;nbsp;멀티모듈 설계 이야기 with Spring, Gradle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring Boot</category>
      <category>maven</category>
      <category>multi module</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/71</guid>
      <comments>https://e-una.tistory.com/71#entry71comment</comments>
      <pubDate>Fri, 16 Dec 2022 01:41:38 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM 알아보기 - (6) Thread, Java Thread Model</title>
      <link>https://e-una.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 자바 코드가 어떤 과정으로 실행되는지 알아보기 위한 &quot;JVM 알아보기&quot; 마지막 포스팅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 &lt;code&gt;Thread&lt;/code&gt;&amp;nbsp;와 &lt;code&gt;Java Thread Model&lt;/code&gt;&amp;nbsp;에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;Thread&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 자바 프로그램을 실행하면 JVM 프로세스가 실행되고, JVM 프로세스 안에서 GC 를 포함한 여러 개의 스레드가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각 스레드마다 Java Stack, PC Register, Native Method Stack 이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이렇게 자바는 동일한 메모리를 읽지만 각각 작업을 수행하는 스레드들, 즉 멀티 스레드를 통해 동시에 여러 작업을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUAka8/btrSPQcovrl/7oaLvc4Pu7XKZh2QLRRImK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUAka8/btrSPQcovrl/7oaLvc4Pu7XKZh2QLRRImK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUAka8/btrSPQcovrl/7oaLvc4Pu7XKZh2QLRRImK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUAka8%2FbtrSPQcovrl%2F7oaLvc4Pu7XKZh2QLRRImK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;272&quot; height=&quot;301&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 왜 프로세스를 여러 개 추가하지 않고 스레드라는 개념을 만든 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 프로세스를 생성하려면 많은 시간과 자원이 필요하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, JVM 은 벤더마다 다르지만 최소 32MB 이상의 메모리가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그에 비해 스레드는 1MB 이내의 메모리를 점유한다.&amp;nbsp;그렇기 때문에 스레드를 경량 프로세스 라고도 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 스레드 모델의 변화와 그 이유를 중점으로 스레드에 대해 알아보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;Multi Threading Model&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드에는 &lt;b&gt;사용자 수준 스레드(User Level Threads)&lt;/b&gt;와 &lt;b&gt;커널 수준 스레드(Kernel Level Threads)&lt;/b&gt; 두 가지 유형이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 두 가지 유형을 간단하게 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 &lt;b&gt;수준&amp;nbsp;&lt;/b&gt;스레드&lt;/b&gt;는 사용자 라이브러리를 통해 사용자가 만든 스레드로, 스레드가 생성 된 프로세스의 주소 공간에서 해당 프로세스에 의해 실행되고 관리된다. 그리고&amp;nbsp;&lt;b&gt;커널 &lt;b&gt;수준&lt;/b&gt;&amp;nbsp;스레드&lt;/b&gt;는 커널에 의해 생성되고 운영체제에 의해 직접 관리된다. 사용자 수준 스레드보다 생성 및 관리 속도가 느리다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 유형의 차이는 누가 스레드를 제어하느냐에 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;사용자 &lt;b&gt;수준&amp;nbsp;&lt;/b&gt;스레드&lt;/b&gt;는 스레드가 생성 된 프로세스 자체에 의해 제어되며, 커널은 프로세스 내의 사용자 스레드에 대해 알지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;커널 &lt;b&gt;수준&amp;nbsp;&lt;/b&gt;스레드&lt;/b&gt;는 OS에 의해 직접 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 스레드를 실행한다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드를 실행하기 위해서는 커널의 CPU 스케줄러가 스레드를 CPU 에 스케줄링 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 CPU 스케줄러는 커널의 일부이기 때문에 사용자 스레드에 대해 알지 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 이유로 사용자 스레드는 커널 스레드에 매핑되어 실행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 신제품 출시에 비유할 수 있다. 우리가 출시하려는 신제품은 사용자 스레드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 제품을 판매하기 위해 제품의 회사를 정부에 등록해야한다. 여기서 회사는 프로세스, 정부는 커널이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사는 상점에 입점을 하고, 입점을 위해서 정부의 규칙을 따라야한다. 이 상점이 커널 스레드다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 커널 스레드(상점)와 프로세스(회사)가 매핑되어 있고, 그 프로세스 내에서 사용자 스레드가 생성되기 때문에 커널에서 인식할 수 있는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 스레드와 사용자 스레드를 매핑하는 이유를 알아보았으니 이제 매핑 방법에 대해 알아보도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매핑 방법에는 일반적으로 세 가지가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Many-to-One Model, 다대일 모델&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTsCf2/btrSKQj3Ku3/019RTLBaq8jl3hD3aj3MI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTsCf2/btrSKQj3Ku3/019RTLBaq8jl3hD3aj3MI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTsCf2/btrSKQj3Ku3/019RTLBaq8jl3hD3aj3MI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTsCf2%2FbtrSKQj3Ku3%2F019RTLBaq8jl3hD3aj3MI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;316&quot; height=&quot;377&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대일 모델은 여러 사용자 스레드를 하나의 커널 스레드에 매핑한다. 스레드는 사용자 공간의 스레드 라이브러리에 의해 관리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 스레드가 Blocking 되는 경우에 전체 프로세스가 Block 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 하나의 스레드만이 커널에 접근할 수 있기 때문에, 멀티 코어 시스템에서 병렬로 실행될 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 동기화 및 리소스 공유가 쉽기 때문에 실행 시간이 단축된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 의 초기 버전의 스레드 모델인 Green Thread 가 다대일 모델을 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 컴퓨터 시스템에서 멀티 코어가 표준이 되면서 그 이점을 살릴 수 없는 Green Thread 는 현재 Java 에서 사용되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;One-to-One Model, 일대일 모델&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OtRNJ/btrSJXDG17H/Yndv5wejjHLd7WUAGb25i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OtRNJ/btrSJXDG17H/Yndv5wejjHLd7WUAGb25i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OtRNJ/btrSJXDG17H/Yndv5wejjHLd7WUAGb25i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOtRNJ%2FbtrSJXDG17H%2FYndv5wejjHLd7WUAGb25i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;427&quot; height=&quot;314&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일대일 모델은 각 사용자 스레드를 각각 하나의 커널 스레드에 매핑한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 하나의 스레드가 Blocking 되더라도 다른 스레드가 실행될 수 있기 때문에 병렬 실행이 용이하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모델의 단점은 사용자 스레드를 만들기 위해서는 커널 스레드를 만들어야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 수의 커널 스레드는 시스템 성능에 부담을 줄 수 있기 때문에 Window 나 Linux 는 스레드 수의 증가를 제한한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Many-to-Many Model, 다대다 모델&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmOIbF/btrSQTNzW5F/P0XB6P0L02C20QRWJkq30k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmOIbF/btrSQTNzW5F/P0XB6P0L02C20QRWJkq30k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmOIbF/btrSQTNzW5F/P0XB6P0L02C20QRWJkq30k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmOIbF%2FbtrSQTNzW5F%2FP0XB6P0L02C20QRWJkq30k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;311&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대다 모델은 여러 개의 사용자 스레드를 그보다 작은 수 혹은 같은 수의 커널 스레드로 멀티플렉스한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커널 스레드의 수는 응용 프로그램에 따라 다르다. 4코어 시스템보다 8코어 시스템에서 더 많은 커널 스레드를 할당받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 두 모델의 단점을 절충한 방법으로, 개발자는 필요한 만큼 사용자 스레드를 생성하고 그에 상응하는 커널 스레드가 병렬로 수행될 수 있다. 가장 높은 정확도의 동시성 처리를 제공하는 모델로, 하나의 스레드가 Blocking 되었을 때 커널은 다른 스레드의 수행을 스케쥴할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 다대다 모델은 two-level-model(두 수준 모델) 이라고도 불리는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대다 모델은 사용자 스레드가 하나의 커널 스레드에만 연결되는 일대일 모델로 동작하는 것도 허용하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;295&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qgeVd/btrSN0sNbOH/crqVqouna1x2kmg4KBwXAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qgeVd/btrSN0sNbOH/crqVqouna1x2kmg4KBwXAk/img.png&quot; data-alt=&quot;two level model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qgeVd/btrSN0sNbOH/crqVqouna1x2kmg4KBwXAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqgeVd%2FbtrSN0sNbOH%2FcrqVqouna1x2kmg4KBwXAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;295&quot; data-origin-width=&quot;538&quot; data-origin-height=&quot;295&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;two level model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다대다 모델이 세 모델 중 가장 융통성있는 것 처럼 보이지만 실제로는 구현하기가 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 대부분의 시스템에서 코어 수가 증가하면서 커널 스레드를 제한하는 것의 중요성이 줄어들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 현재 대부분의 운영체제는 일대일 모델을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 현재 자바의 스레드는 어떤 모델을 사용할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 JDK 1.3 이후로 다대다 모델을 사용한다.&amp;nbsp;이를 Native Thread 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;Green Thread vs. Native Thread&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바는 초기에는 Green Thread 를 사용하다가 Native Thread 로 스레드 모델을 변경하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 두 스레드 모델의 차이점을 정리하고 포스팅을 마치도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;407&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BanD4/btrSKvug5fT/8XUA03bCnXGXmG3LRqMzV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BanD4/btrSKvug5fT/8XUA03bCnXGXmG3LRqMzV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BanD4/btrSKvug5fT/8XUA03bCnXGXmG3LRqMzV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBanD4%2FbtrSKvug5fT%2F8XUA03bCnXGXmG3LRqMzV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;834&quot; height=&quot;407&quot; data-origin-width=&quot;834&quot; data-origin-height=&quot;407&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그린 스레드는 커널의 개입 없이 사용자 레벨에서 관리하는 스레드 모델을 의미하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이티브 스레드는 현재 우리가 사용하는 스레드를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 수준에서 JVM 이 완전하게 관리하는 그린 스레드 모델은 멀티 코어 시스템이 보편화되면서 자바에서 사라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;5. Reference&quot; data-ke-size=&quot;size26&quot;&gt;&lt;u&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/u&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javatpoint.com/multithreading-models-in-operating-system&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JavaTpoint, Multithreading Models in Operating system&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/why-must-user-threads-be-mapped-to-a-kernel-thread/?ref=rp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeeksForGeeks,&amp;nbsp;Why must user threads be mapped to a kernel thread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/green-vs-native-threads-and-deprecated-methods-in-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeeksForGeeks,&amp;nbsp;Green vs Native Threads and Deprecated Methods in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://softwareengineering.stackexchange.com/questions/120384/why-not-green-threads&quot;&gt;Why not Green Threads?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/47700749/whats-the-difference-between-green-threads-and-user-threads&quot;&gt;What's the difference between 'green threads' and 'user threads'&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;Operating System Concepts (10e)&lt;/i&gt;&amp;nbsp;4.3 다중 스레드 모델&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/70</guid>
      <comments>https://e-una.tistory.com/70#entry70comment</comments>
      <pubDate>Mon, 5 Dec 2022 14:11:54 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM 알아보기 - (5) JVM GC 유형, G1 GC</title>
      <link>https://e-una.tistory.com/69</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 자바 코드가 어떤 과정으로 실행되는지 알아보기 위한 &quot;JVM 알아보기&quot; 다섯 번째 포스팅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 &lt;code&gt;JVM GC&lt;span&gt;의 여러 유형&lt;/span&gt;&lt;/code&gt;에 대해 살펴보고, 그 중 &lt;code&gt;G1 GC&lt;/code&gt; 에 대해 자세히 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서 이야기한 것과 같이 GC 에는 여러 유형이 있다. 그 중 몇 가지를 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;Serial GC&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬 GC&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LnVh7/btrShBVA2sI/Fa3mCyuwHxApMImtmsA4B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LnVh7/btrShBVA2sI/Fa3mCyuwHxApMImtmsA4B0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LnVh7/btrShBVA2sI/Fa3mCyuwHxApMImtmsA4B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLnVh7%2FbtrShBVA2sI%2FFa3mCyuwHxApMImtmsA4B0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;426&quot; height=&quot;298&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 스레드로 작동하는 가장 간단한 GC 다. 따라서 다중 스레드 프로그램에서는 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 스레드를 사용하면 여러 스레드 간에 통신 오버헤드가 없기 때문에 효율성이 높다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역의 GC 는 지난 포스팅에서 설명한 방식과 같고, Old 영역의 GC 는 Mark-and-Sweep-Compact 알고리즘을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리가 적고 CPU 코어의 개수가 적은 간단한 프로그램에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Parallel GC&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 GC, Throughput(처리량) GC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CWxMW/btrSkDlauor/uufgbQDHdSkGMIdrl8GBG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CWxMW/btrSkDlauor/uufgbQDHdSkGMIdrl8GBG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CWxMW/btrSkDlauor/uufgbQDHdSkGMIdrl8GBG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCWxMW%2FbtrSkDlauor%2FuufgbQDHdSkGMIdrl8GBG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;368&quot; data-origin-width=&quot;1096&quot; data-origin-height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Parallel GC 는 Java 8 의 default JVM 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Serial GC 와 알고리즘은 같지만, 다중 스레드로 동작한다는 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 CPU 를 사용하여 처리 속도가 높기 때문에 처리량 GC 라고도 불린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대량 데이터 처리, 일괄 처리와 같은 긴 프로세스를 실행하고 긴 STW(Stop-The-World) 가 허용되는 프로그램에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션이 최고의 처리량을 달성해야하고, 1초 이상의 일시적인 작업 중지가 허용된다면 이 GC 를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;G1 GC&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Garbage First GC&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;656&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n1oju/btrSq9qatok/xCAtmhoe2Ufxi7gGkPUPMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n1oju/btrSq9qatok/xCAtmhoe2Ufxi7gGkPUPMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n1oju/btrSq9qatok/xCAtmhoe2Ufxi7gGkPUPMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn1oju%2FbtrSq9qatok%2FxCAtmhoe2Ufxi7gGkPUPMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;433&quot; height=&quot;227&quot; data-origin-width=&quot;1250&quot; data-origin-height=&quot;656&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;G1 GC 는 Java 9 의 default JVM 으로, 다른 GC 들과는 Heap 의 구조가 매우 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙을 동일한 크기의 여러 영역(Region)으로 나누고 각 영역에 객체를 할당하며, 영역 별로 GC 를 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 가비지 컬렉터가 사용하던 Eden, Survivor 등의 세대 별 영역도 동일한 크기의 여러 영역을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Humongous Region 은 하나의 Region 의 50% 이상 크기를 가진 객체에 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;G1 은 힙 전체에서 살아있는 객체와 가비지를 식별하기 위해 Concurrent Global Marking(동시 글로벌 마킹)을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마킹이 완료되면 회수 가능한 객체, 즉 가비지로 가득 차 있을 가능성이 큰 힙 영역부터 Sweep 을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;G1 GC 는 메모리 공간이 큰(힙 크기가 4GB 이상인) 다중 프로세서 시스템 용으로 설계되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;영역 별로 GC 를 수행하기 때문에 STW&amp;nbsp;가 짧아 &lt;span&gt;튜닝이 거의 필요하지 않고,&amp;nbsp;&lt;/span&gt;높은 처리량을 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;G1 에서 사용하는 자료구조&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Remember Set(RSet)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙에는 Region 마다 RSet 가 존재한다. &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조를 가진 객체들이 어느 영역에 있는지 추적하기 위한 자료구조다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Collection Set(CSet)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 에서 수집 될 Region 의 집합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;G1 GC 의 프로세스&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. G1 Heap 구조&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP2Cvf/btrSmZvCnG2/RGwIrGATg2xs0gJ5HjgoY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP2Cvf/btrSmZvCnG2/RGwIrGATg2xs0gJ5HjgoY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP2Cvf/btrSmZvCnG2/RGwIrGATg2xs0gJ5HjgoY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP2Cvf%2FbtrSmZvCnG2%2FRGwIrGATg2xs0gJ5HjgoY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;236&quot; height=&quot;267&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙은 여러 Region 으로 나뉜다. 영역의 크기는 JVM 이 결정하는데, 각 영역의 크기는 최소 1MB 부터 최대 32MB 까지 다양하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. G1 Heap 할당&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6Qws/btrSms5PUac/ayfmEpayBWYbCIxJlQLaE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6Qws/btrSms5PUac/ayfmEpayBWYbCIxJlQLaE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6Qws/btrSms5PUac/ayfmEpayBWYbCIxJlQLaE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6Qws%2FbtrSms5PUac%2FayfmEpayBWYbCIxJlQLaE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;493&quot; height=&quot;348&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Region 은 Eden, Survivor 등의 논리적 영역으로 매핑된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. G1 의 Young GC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZXDF4/btrSnWZQafF/5gAoWlQ70SUJFs7pqfZyG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZXDF4/btrSnWZQafF/5gAoWlQ70SUJFs7pqfZyG1/img.png&quot; data-alt=&quot;Young GC 수행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZXDF4/btrSnWZQafF/5gAoWlQ70SUJFs7pqfZyG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZXDF4%2FbtrSnWZQafF%2F5gAoWlQ70SUJFs7pqfZyG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;320&quot; data-origin-width=&quot;1132&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Young GC 수행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q4z9s/btrSrvzVU46/qKLbRfjs9fglr3OVvMivFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q4z9s/btrSrvzVU46/qKLbRfjs9fglr3OVvMivFk/img.png&quot; data-alt=&quot;Young GC 수행 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q4z9s/btrSrvzVU46/qKLbRfjs9fglr3OVvMivFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq4z9s%2FbtrSrvzVU46%2FqKLbRfjs9fglr3OVvMivFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;486&quot; height=&quot;325&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Young GC 수행 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 방식과 같이 Eden 공간이 가득차면 Survivor 공간으로 살아있는 객체를 복사하고, 일정 Age bit 가 넘어가면 Old 영역으로 promotion 된다. 하나 이상의 영역이 하나의 영역으로 복사되면서 압축된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young GC 가 일어날 때는 STW 가 발생하며, 여러 스레드를 사용해서 병렬 수행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. G1 의 Old GC&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;G1 의 Old GC 는 다섯 가지 프로세스로 수행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Initial Mark(STW)&lt;/b&gt;: Old Region 에 있는 객체에서 참조하는 Survivor Region (Root Region) 을 찾는다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Root Region Scanning&lt;/b&gt;: 이전 단계(Initial Mark)에서 마크 해두었던 Root Region 을 스캔한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Concurrent Marking&lt;/b&gt;: 전체 힙에서 살아있는 객체를 찾고 빈 Region 을 표시한다. 멀티 스레드로 동작하며, 애플리케이션과 동시에 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;686&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qIQoH/btrSm1NVdcv/x4jhKAE9sQ6MFobmpsNQmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qIQoH/btrSm1NVdcv/x4jhKAE9sQ6MFobmpsNQmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qIQoH/btrSm1NVdcv/x4jhKAE9sQ6MFobmpsNQmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqIQoH%2FbtrSm1NVdcv%2Fx4jhKAE9sQ6MFobmpsNQmK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;324&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;686&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-type=&quot;image&quot; data-ke-style=&quot;alignCenter&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;b&gt;Remark(STW)&lt;/b&gt;: 힙에서 살아있는 객체에 마킹을 완료한다. 빈 Region 은 제거된다. 멀티 스레드로 동작하며 SATB(Snapshot-at-the-Beginning) 알고리즘을 사용한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CleanUp(STW)&lt;/b&gt;: 가비지가 제일 많은 Region 을 정리한다. 해당 Region 은 Young GC 와 동시에 수집되는데, 이를 Mixed GC 라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/leRjv/btrSs00BeEv/I7tEkvwB4uIWFrrUk3o8Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/leRjv/btrSs00BeEv/I7tEkvwB4uIWFrrUk3o8Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/leRjv/btrSs00BeEv/I7tEkvwB4uIWFrrUk3o8Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FleRjv%2FbtrSs00BeEv%2FI7tEkvwB4uIWFrrUk3o8Sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;338&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모든 Old GC 의 프로세스가 끝나면 다음과 같이 압축된 상태로 정리된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yNAx0/btrSnFD2vbG/JyAluD2UcqhzMpXoWjUlbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yNAx0/btrSnFD2vbG/JyAluD2UcqhzMpXoWjUlbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yNAx0/btrSnFD2vbG/JyAluD2UcqhzMpXoWjUlbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyNAx0%2FbtrSnFD2vbG%2FJyAluD2UcqhzMpXoWjUlbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;381&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 JVM GC 의 몇 가지 유형과 Java 9 의 default GC 인 G1 GC 에 대해서 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Region 별로 GC 를 수행하여 STW 시간을 줄였고, 하나 이상의 Region 을 하나로 압축하면서 파편화를 방지하는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에서는 JVM 의 Thread 를 다루겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;5. Reference&quot; data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.javatpoint.com/types-of-garbage-collector-in-java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Javatpoint,&amp;nbsp;Types of Garbage Collector in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Getting Started with the G1 Garbage Collector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/jvm-garbage-collectors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung, JVM Garbage Collectors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/1329&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Naver D2, Java Garbage Collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://initproc.tistory.com/entry/G1-Garbage-Collection&quot;&gt;G1 Garbage Collection (G1 GC)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/69</guid>
      <comments>https://e-una.tistory.com/69#entry69comment</comments>
      <pubDate>Tue, 29 Nov 2022 22:06:54 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM 알아보기 - (4) JVM GC, Heap</title>
      <link>https://e-una.tistory.com/68</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 자바 코드가 어떤 과정으로 실행되는지 알아보기 위한 &quot;JVM 알아보기&quot; 네 번째 포스팅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 JVM 의&amp;nbsp;&lt;code&gt;GC&lt;/code&gt; 와 &lt;code&gt;Heap&lt;/code&gt; 에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;JVM GC(Garbage Collection)&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 직접 자바의 메모리 관리를 하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 관리를 JVM 이 알아서 하기 때문인데, JVM 내에서 메모리 관리를 해주는 것을 &lt;i&gt;가비지 컬렉터&amp;nbsp;&lt;/i&gt;라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가비지 컬렉터는 힙에서 사용 가능한 모든 객체를 추적하고 사용하지 않는 객체에 할당 된 메모리를 해제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업을 &lt;i&gt;가비지 컬렉션, GC&lt;/i&gt; 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 의 프로세스를 알아보기 이전에, 먼저 GC 와 관련 된 개념들을 정리해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;UnReachable Object&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유효한 참조가 없는 객체를 UnReachable Object(도달할 수 없는 객체)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp;UnReachable 하다고 간주되는 객체는 GC 의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 유효한 참조를 판단하는 기준은 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 JVM 의 GC 는 힙에서 사용 가능한 모든 객체를 추적한다고 했다. GC 는 객체 그래프를 순회하며 모든 객체를 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프를 순회하려면 시작점이 필요한데 이 시작점을 GC의 root 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 GC 루트에서 직접 또는 간접적으로 참조되는 모든 객체는 Reachable 한 객체(도달할 수 있는 객체)로 구분되어 GC 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로&amp;nbsp;&lt;u&gt;GC 루트에서 참조되지 않는 UnReachable 한 객체들은 사용하지 않는다고 간주되어 GC 의 대상&lt;/u&gt;이&amp;nbsp;된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC root 는 주로 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class, 클래스 변수&lt;/li&gt;
&lt;li&gt;Java Stack, 메서드 실행 시에 사용하는 지역 변수와 파라미터들&lt;/li&gt;
&lt;li&gt;메서드 영역의 정적 변수&lt;/li&gt;
&lt;li&gt;Native Stack, JNI(Java Native Interface) 에 의해 생성된 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;GC 의 대상이 되는 객체&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;null 로 선언 된 참조 변수&lt;/li&gt;
&lt;li&gt;재할당 된 참조 변수&lt;/li&gt;
&lt;li&gt;메서드 내부에서 생성된 객체&lt;/li&gt;
&lt;li&gt;고립된 섬(Island of Isolation) : 객체 A 와 B 가 서로만을 참조할 때 두 객체 모두 호출할 수 있는 방법이 없어진 경우 또는 참조되지 않는 단일 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;432&quot;&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/island-of-isolation-in-java/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsKj8R/btrR3d72kpU/JY3kVm2TSgZKBXgVJoPEg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsKj8R%2FbtrR3d72kpU%2FJY3kVm2TSgZKBXgVJoPEg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;174&quot; data-origin-width=&quot;1640&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/a&gt;&lt;figcaption&gt;Island of Isolation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Island of Isolation 의 예시를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 두 개 만들어서 서로만을 참조하게 한 뒤, 두 객체를 모두 null 로 만들고 가비지 컬렉터를 호출해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Java&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669307779854&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Main class
public class GFG {
 
    GFG i;
 
    // Method 1
    // Main driver method
    public static void main(String[] args)
    {
 
        // Creating object of class inside main() method
        GFG t1 = new GFG();
        GFG t2 = new GFG();
 
        // Object of t1 gets a copy of t2
        t1.i = t2;
 
        // Object of t2 gets a copy of t1
        t2.i = t1;
 
        // Till now no object eligible
        // for garbage collection
        t1 = null;
 
        // Now two objects are eligible for
        // garbage collection
        t2 = null;
 
        // Calling garbage collector
        System.gc();
    }
 
    // Method 2
    // overriding finalize() Method
    @Override protected void finalize() throws Throwable
    {
        // Print statement
        System.out.println(&quot;Finalize method called&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;Output&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1669307890291&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Finalize method called
Finalize method called&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 파괴하기 전에 가비지 컬렉터는 finalize 메서드를 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t1 만 null 로 만들고 가비지 컬렉터를 호출하면 GC 가 동작하지 않는다. t2 라는 외부 참조가 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;t1 과 t2 가 모두 null 이 되면 두 객체를 호출할 수 있는 방법이 없으므로 두 객체 모두 GC 의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;stop-the-world&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 는 root 에서부터 객체 그래프를 순회하며 객체가 참조되고 있는지 추적한다. 그리고 참조되지 않는 객체를 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은 애플리케이션이 실행되는 중에 병행되어야 하는데, 그러기 위해서는 GC 이외의 모든 쓰레드의 작업을 중단해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 를 실행하기 위해 JVM 이 &lt;u&gt;GC 를 실행하는 쓰레드 이외의 모든 쓰레드의 작업을 멈추는 것&lt;/u&gt;을 stop-the-world 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업이 완료될 때까지 모든 작업이 중지되기 때문에 성능 저하의 원인이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 stop-the-world 의 작업 시간을 줄이는 것이 GC 튜닝의 주요 목적이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;JVM GC 의 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Mark and Sweep&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 GC 는 Mark and Sweep 이라는 두 가지 프로세스로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BD5pg/btrScbhS5Pe/yiNFkLDjk6K2vHEbq4uSCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BD5pg/btrScbhS5Pe/yiNFkLDjk6K2vHEbq4uSCk/img.png&quot; data-alt=&quot;Marking&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BD5pg/btrScbhS5Pe/yiNFkLDjk6K2vHEbq4uSCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBD5pg%2FbtrScbhS5Pe%2FyiNFkLDjk6K2vHEbq4uSCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;509&quot; data-origin-width=&quot;872&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Marking&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V2neE/btrSbT2Fe2s/8ISNK22mZYit2kbIikxjhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V2neE/btrSbT2Fe2s/8ISNK22mZYit2kbIikxjhk/img.png&quot; data-alt=&quot;Sweep&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V2neE/btrSbT2Fe2s/8ISNK22mZYit2kbIikxjhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV2neE%2FbtrSbT2Fe2s%2F8ISNK22mZYit2kbIikxjhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;416&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sweep&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Marking 단계에서 사용 중인 메모리와 그렇지 않은 메모리를 식별하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sweep 단계에서 사용하지 않는다고 식별 된 메모리를 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rbUHr/btrSeNOnTGI/R18jMpSAHAzcv5UwxbNud0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rbUHr/btrSeNOnTGI/R18jMpSAHAzcv5UwxbNud0/img.png&quot; data-alt=&quot;compacting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rbUHr/btrSeNOnTGI/R18jMpSAHAzcv5UwxbNud0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrbUHr%2FbtrSeNOnTGI%2FR18jMpSAHAzcv5UwxbNud0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;446&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;compacting&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 메모리 할당 시의 성능을 위해 참조되지 않는 객체를 삭제한 뒤에 빈 공간을 압축하는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;GC 와 Heap&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Weak Generational Hypothesis, 약한 세대 가설&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 는 Heap 영역 내의 모든 객체들을 대상으로 동작한다.&amp;nbsp;그리고 실행 중인 애플리케이션은 시간이 지남에 따라 더 많은 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 시간이 지날수록 점점 더 객체가 많아지고 GC 작업의 시간이 증가할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 JVM 은 객체의 세대를 분리했다. 다음 그래프를 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P26co/btrScbIZGVa/rZbMc6fgyVKDw33nneVK7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P26co/btrScbIZGVa/rZbMc6fgyVKDw33nneVK7K/img.png&quot; data-alt=&quot;객체 수명에 대한 일반적인 분포&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P26co/btrScbIZGVa/rZbMc6fgyVKDw33nneVK7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP26co%2FbtrScbIZGVa%2FrZbMc6fgyVKDw33nneVK7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;371&quot; height=&quot;258&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;객체 수명에 대한 일반적인 분포&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X 축은 객체의 수명을, Y 축은 살아있는 객체 수(할당 된 총 바이트 수)를 나타낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프의 분포를 보면 왼쪽에 수명이 짧은 객체가 엄청나게 많다. 대부분의 객체는 짧은 시간동안 사용되고 메모리가 해제되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 weak generational hypothesis 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전제를 통해 GC 의 효율적인 수집이 가능하다. 객체의 세대를 분리하여 각각 GC 를 수행하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7Z56F/btrR9YwP65t/eGbssuFuPNvH9RiugwVaAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7Z56F/btrR9YwP65t/eGbssuFuPNvH9RiugwVaAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7Z56F/btrR9YwP65t/eGbssuFuPNvH9RiugwVaAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7Z56F%2FbtrR9YwP65t%2FeGbssuFuPNvH9RiugwVaAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;857&quot; height=&quot;318&quot; data-origin-width=&quot;857&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Young 영역&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, Young 영역부터 알아보자.&amp;nbsp;이 영역에는 새로 생성한 객체의 대부분이 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Young 영역의 GC 프로세스는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eden 영역에 객체가 생성된다.&lt;/li&gt;
&lt;li&gt;Eden 영역이 가득 차면 GC가 발생하여 Mark and Sweep 의 과정을 거쳐서 살아있는 객체만 Survivor 영역 중 하나로 이동되고, 다시 Eden 영역을 채운다.&lt;/li&gt;
&lt;li&gt;이 과정을 반복하여 하나의 Survivor 영역이 가득 차게 되면 살아있는 객체만 나머지 비어있는 Survivor 영역으로 이동된다. 이때, Eden 영역에 있는 살아있는 객체들도 비어있던 Survivor 영역으로 간다. 즉, Survivor 영역의 둘 중 하나는 반드시 비어있어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이와 같이 Young 영역에서 발생하는 GC 를 Minor GC 라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Minor GC 는 대부분의 객체가 가비지일 것을 가정하고 최적화되어 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 더 작은 공간이 할당되고, 많은 객체를 검사하지 않기 때문에 속도가 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇 차례의 Minor GC 가 수행되고도 살아남은 객체들은 Old 영역으로 이동하게 된다. 이를 Promotion 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;각 객체는 Minor GC 에서 살아남은 횟수를 기록하는 age bit 를 가지고 있고, Minor GC 가 발생할 때마다 age bit 값이 1씩 증가한다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;age bit 값이 MaxTenuringThreshold 라는 설정값을 초과하게 되는 경우 객체가 이동된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Old 영역&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old 영역에서 수행되는 GC 는 Major GC 라고 한다. 모든 라이브 객체를 수집해야 하기 때문에 훨씬 속도가 느리며, 수행 빈도도 적다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Old 영역의 GC 는 GC 방식에 따라 처리 프로세스가 달라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GC 방식에는 여러 유형이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Serial GC&lt;/li&gt;
&lt;li&gt;Parallel GC&lt;/li&gt;
&lt;li&gt;Parallel Old GC (Parallel Compacting GC)&lt;/li&gt;
&lt;li&gt;Concurrent Mark and Sweep (CMS)&lt;/li&gt;
&lt;li&gt;G1(Garbage First) GC&lt;/li&gt;
&lt;li&gt;그 외의 여러 유형&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;u&gt;마치며&lt;/u&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 JVM 의 GC 와 JVM Heap 의 구조에 대해 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에는 GC 의 유형 별 프로세스와 장단점에 대해 다루도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;u&gt;Reference&lt;/u&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Java Garbage Collection Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/jvm-garbage-collectors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Baeldung,&amp;nbsp;JVM Garbage Collectors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/garbage-collection-java/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeeksforGeeks,&amp;nbsp;Garbage Collection in Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/generations.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/1329&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Naver D2, Java Garbage Collection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>GC</category>
      <category>heap</category>
      <category>jvm</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/68</guid>
      <comments>https://e-una.tistory.com/68#entry68comment</comments>
      <pubDate>Sun, 27 Nov 2022 23:51:10 +0900</pubDate>
    </item>
    <item>
      <title>[Java] JVM 알아보기 - (3) JVM Memory Structure</title>
      <link>https://e-una.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 만든 자바 코드가 어떤 과정으로 실행되는지 알아보기 위한 &quot;JVM 알아보기&quot; 세 번째 포스팅입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 포스팅에서는 JVM 메모리 구조에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;JVM Architecture&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMrM4c/btrRMzwjPzw/pihAu9zAkfc70apOipbqe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMrM4c/btrRMzwjPzw/pihAu9zAkfc70apOipbqe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMrM4c/btrRMzwjPzw/pihAu9zAkfc70apOipbqe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMrM4c%2FbtrRMzwjPzw%2FpihAu9zAkfc70apOipbqe1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;838&quot; height=&quot;590&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 포스팅에서 이야기한 것과 같이, ClassLoader 는 &quot;.class&quot; 파일에서 바이너리 데이터를 생성하여 Method Area 에 저장한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM Architecture 그림을 보면 Method Area 는 Runtime Data Areas 의 한 부분이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM 은 OS 로부터 프로그램 실행 중에 사용되는 메모리 영역을 할당받는데, 이를 Runtime Data Areas 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;앞으로 설명할 Method Area 나 Heap Area 는 JVM 의 스펙에 정의되어 있는 논리적 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;모든 JVM 에 Method Area 가 있지만, &quot;여기가 Method Area 입니다.&quot; 라는 것이 아니라&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;JVM 스펙에 따라 정해진 역할을 하는 영역을 Method Area 라고 논리적 개념으로 설명하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;(참고)&amp;nbsp;&lt;a style=&quot;color: #9d9d9d;&quot; href=&quot;https://stackoverflow.com/questions/50163218/is-method-area-still-present-in-java-8&quot;&gt;Is Method area still present in Java 8?&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;&amp;nbsp;Runtime Data Areas&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Runtime Data Areas 의 공유 자원으로 Heap Area 와 Method Area 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 모든 Thread 가 이 영역을 공유한다는 의미이며, 모든 Thread 가 공유하기 때문에 동기화 문제가 발생할 수 있다는 의미이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Heap Area&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 영역은 JVM 에서 가장 중요한 메모리 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 객체를 생성(메모리 할당) 및 저장하는 공간&lt;/b&gt;으로, JVM 이 시작될 때 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 new 키워드를 사용해서 객체를 생성하면 JVM 이 객체의 인스턴스를 힙에 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1669016675239&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;StringBuilder builder = new StringBuilder();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙 영역에 생성된 객체와 배열은 스택 영역의 변수나 다른 객체의 필드에서 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 JVM 프로세스에는 단 하나의 Heap 영역만 있으며,&amp;nbsp;&lt;b&gt;GC(Garbage Collection) 의 대상&lt;/b&gt;이 되는 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 더 이상 사용되지 않거나 명시적으로 null 로 선언하면 의미 없는 객체가 되어 GC 의 대상이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Method Area&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 영역에는 클래스 코드, 클래스 이름, 부모 클래스 이름, 메서드, 생성자, 변수 정보 등 모든 클래스 수준 정보가 저장 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;클래스와 클래스의 메타 데이터를 저장한다&lt;/b&gt;고 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 영역에는 &lt;b&gt;Runtime Constant Pool&lt;/b&gt; 이라는 집합이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리터럴(문자열, 정수 및 부동 소수점 상수) 및 필드, 메서드에 대한 심볼릭 레퍼런스*를 저장하며,&amp;nbsp;이후에 배울 Stack 의 Frame 에서 참조한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*심볼릭 레퍼런스란?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;참조하는 대상의 이름. 사용 전에는 이름만 저장되어 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;클래스 접근 필요 시 클래스 로더가 Linking 하여 메모리 상의 실제 주소로 연결된다. (동적으로 로드)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;이를 R&lt;span&gt;&lt;span&gt;&lt;span&gt;esolution 이라고 부른다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Constant Pool 을 확인하기 위해 다음과 같이 간단한 클래스를 생성해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1669127293524&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package ex;

public class Student {
    private int age = 29;

    public void study() {
        System.out.println(&quot;studying....&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1669023648140&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package ex;

public class School {
    public static void main(String[] args) {
        Student student = new Student();
        student.study();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;School 클래스를 역어셈블해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1669127398097&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;javap -v [Class Name]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 Constant pool 을 포함한 클래스 파일의 여러 정보를 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ7Rpb/btrRTFJC6OZ/cgScAUwvTGGdWMfujCXXvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ7Rpb/btrRTFJC6OZ/cgScAUwvTGGdWMfujCXXvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ7Rpb/btrRTFJC6OZ/cgScAUwvTGGdWMfujCXXvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ7Rpb%2FbtrRTFJC6OZ%2FcgScAUwvTGGdWMfujCXXvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;357&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역어셈블링하여 확인한 School 클래스 파일의 Constant pool 은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1669024867818&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object.&quot;&amp;lt;init&amp;gt;&quot;:()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // &quot;&amp;lt;init&amp;gt;&quot;:()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               &amp;lt;init&amp;gt;
   #6 = Utf8               ()V
   #7 = Class              #8             // ex/Student
   #8 = Utf8               ex/Student
   #9 = Methodref          #7.#3          // ex/Student.&quot;&amp;lt;init&amp;gt;&quot;:()V
  #10 = Methodref          #7.#11         // ex/Student.study:()V
  #11 = NameAndType        #12:#6         // study:()V
  #12 = Utf8               study
  #13 = Class              #14            // ex/School
  #14 = Utf8               ex/School
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               LocalVariableTable
  #18 = Utf8               this
  #19 = Utf8               Lex/School;
  #20 = Utf8               main
  #21 = Utf8               ([Ljava/lang/String;)V
  #22 = Utf8               args
  #23 = Utf8               [Ljava/lang/String;
  #24 = Utf8               student
  #25 = Utf8               Lex/Student;
  #26 = Utf8               SourceFile
  #27 = Utf8               School.java&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#(해시) 로 참조하고 있는 상수 풀의 인덱스를 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스와 타입, 그리고 매핑하는 값을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669127602861&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#8 = Utf8               ex/Student&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#8 을 보면 참조하는 대상의 이름을 문자열로 가지고 있다. 이를 심볼릭 레퍼런스를 저장한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Constant pool 은 모든 타입, 필드, 메서드에 대한 심볼릭 레퍼런스를 저장하고 있기 때문에 자바 프로그램에서 중요한 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로는 Thread 마다 1개씩 존재하는 Java Stack, PC(Program Counter) Register, Native Method Stack 에 대해서 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Thread 별로 메모리를 할당하기 때문에 동시성 문제에서 자유롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Java Stack&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 Thread&amp;nbsp;에는 Thread 와 동시에 할당된 Java Stack 이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드 호출 시 필요한 수행 정보를 저장하는 역할로, 메서드 정보와 지역 변수, 매개변수, 연산 중 발생하는 임시 데이터를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 타입 변수는 스택 영역에 직접 값을 가지며, 참조 타입 변수는 힙 영역이나 메서드 영역의 객체 주소를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택 영역에서는 메서드 호출 시 필요한 정보를 저장하기 위해 &lt;b&gt;Frame&lt;/b&gt; 을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드가 호출 될때마다 각각의 스택 프레임이 생성되는데, 각 프레임은 하나의 메서드에 대한 정보를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드를 호출할 때마다 프레임을 추가하고, 메서드가 종료되면 해당 프레임을 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java Stack 의 구조를 그림으로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sCRev/btrRKJzE5kw/RDFVUEZFBrj5Z8SQzXGFp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sCRev/btrRKJzE5kw/RDFVUEZFBrj5Z8SQzXGFp1/img.png&quot; data-alt=&quot;출처:https://8iggy.tistory.com/229&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sCRev/btrRKJzE5kw/RDFVUEZFBrj5Z8SQzXGFp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsCRev%2FbtrRKJzE5kw%2FRDFVUEZFBrj5Z8SQzXGFp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;247&quot; data-origin-width=&quot;1882&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:https://8iggy.tistory.com/229&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 통해 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1669133492452&quot; class=&quot;angelscript&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class test {
    public static void main(String[] args) {
        int num1 = 1;
        int num2 = 2;
        int num3 = 3;

        num1 = num1 + num2 + num3;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역어셈블링하여 바이트코드를 확인해보면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1669133492453&quot; class=&quot;angelscript&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; 0: iconst_1
 1: istore_1
 2: iconst_2
 3: istore_2
 4: iconst_3
 5: istore_3
 6: iload_1
 7: iload_2
 8: iadd
 9: iload_3
10: iadd
11: istore_1
12: return&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한줄씩 설명하면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dov4Ph/btrRRvhgEet/oqL4gzldSXCHtjKIlmrOI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dov4Ph/btrRRvhgEet/oqL4gzldSXCHtjKIlmrOI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dov4Ph/btrRRvhgEet/oqL4gzldSXCHtjKIlmrOI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdov4Ph%2FbtrRRvhgEet%2FoqL4gzldSXCHtjKIlmrOI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1092&quot; height=&quot;490&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXr8r/btrRSgDFrVJ/1lKXWjbxjowCpx7x4Y6kEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXr8r/btrRSgDFrVJ/1lKXWjbxjowCpx7x4Y6kEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXr8r/btrRSgDFrVJ/1lKXWjbxjowCpx7x4Y6kEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXr8r%2FbtrRSgDFrVJ%2F1lKXWjbxjowCpx7x4Y6kEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1162&quot; height=&quot;485&quot; data-origin-width=&quot;1162&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 바이트코드 0~5행까지 num1, num2, num3 을 Local Variable Array 에 저장하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6행부터는 Local Variable Array 에서 값을 가져와서 연산을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctLRcM/btrRTs4HXHm/2eVk8wXhaXIKGr3WGDovtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctLRcM/btrRTs4HXHm/2eVk8wXhaXIKGr3WGDovtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctLRcM/btrRTs4HXHm/2eVk8wXhaXIKGr3WGDovtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctLRcM%2FbtrRTs4HXHm%2F2eVk8wXhaXIKGr3WGDovtk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1168&quot; height=&quot;491&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EiefV/btrRRBhswbz/75aFDyzQ1T03kBtps8WLFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EiefV/btrRRBhswbz/75aFDyzQ1T03kBtps8WLFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EiefV/btrRRBhswbz/75aFDyzQ1T03kBtps8WLFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEiefV%2FbtrRRBhswbz%2F75aFDyzQ1T03kBtps8WLFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1185&quot; height=&quot;504&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GDxCS/btrRSPy4VGa/2SXAJ26VhE5YbQ3liikGC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GDxCS/btrRSPy4VGa/2SXAJ26VhE5YbQ3liikGC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GDxCS/btrRSPy4VGa/2SXAJ26VhE5YbQ3liikGC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGDxCS%2FbtrRSPy4VGa%2F2SXAJ26VhE5YbQ3liikGC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1170&quot; height=&quot;479&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드의 지역 변수는 Local Variables 라는 배열에 저장이 되고, 연산이 필요한 경우 Operand Stack(피연산자 스택)에 쌓아 연산 후 Local Variables 배열에 최종 값을 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수가 객체인 경우, Local Variables 에 Heap 에 저장된 인스턴스의 참조 값이 저장되게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;PC Register&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드가 각각의 메서드를 수행할 때, 각각의 명령어 주소값을 저장하는 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드마다 PC Register 를 가지고 있으며, JVM 이 현재 수행 중인 명령어의 주소를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 실행하고 있는 메서드의 몇 번째 줄을 실행해야 하는지 나타낸다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Native Method Stacks&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java 가 아닌 다른 언어로 작성 된 메서드를 위한 메모리 영역이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;마치며&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 JVM 의 메모리 구조 중에서도 Runtime Data Area 의 구성요소에 대해 자세하게 알아보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 포스팅에는 Heap 의 구조과 GC 를 다루도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/u&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.geeksforgeeks.org/java-memory-management/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GeeksforGeeks Java Memory Management&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jamesdbloom.com/JVMInternals.html#constant_pool&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JVM Internals - constant pool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://coding-factory.tistory.com/830&quot;&gt;[Java] 메모리 구조 메소드(Method), 스택(Stack), 힙(Heap) 영역에 대하여&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://youtu.be/UzaGOXKVhwU&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[10분 테코톡]  무민의 JVM Stack &amp;amp; Heap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jithub.tistory.com/40&quot;&gt;자바 메모리 구조(Runtime Data Area)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <author>e-una</author>
      <guid isPermaLink="true">https://e-una.tistory.com/67</guid>
      <comments>https://e-una.tistory.com/67#entry67comment</comments>
      <pubDate>Wed, 23 Nov 2022 01:23:50 +0900</pubDate>
    </item>
  </channel>
</rss>