<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Binceline</title>
    <link>https://teraphonia.tistory.com/</link>
    <description>안녕하세요~~~</description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 01:36:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Binceline</managingEditor>
    <image>
      <title>Binceline</title>
      <url>https://tistory1.daumcdn.net/tistory/1171448/attach/f796fd2a301d481d9c9f7f8df0b1b13b</url>
      <link>https://teraphonia.tistory.com</link>
    </image>
    <item>
      <title>[Kotlin] Interface와 Class</title>
      <link>https://teraphonia.tistory.com/810</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Interface&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/h4&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;추상함수(Abstract) 가질 수 있음&lt;/li&gt;
&lt;li&gt;함수 본문 구현 가능&lt;/li&gt;
&lt;li&gt;여러 interface에서 같은 함수 가질 수 있음&lt;/li&gt;
&lt;li&gt;추상 property 가질 수 있음&lt;/li&gt;
&lt;/ul&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;여러 인터페이스에 동일한 이름의 추상 함수가 선언되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, 다중 상속한 클래스에서는 어떻게 구분해서 호출하는지 다음을 보면 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1653603495318&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun main(args: Array&amp;lt;String&amp;gt;) {
	val base: Base = Base()
    base.printA() // AAAAA
    base.printB() // BBBBB
    base.printC() // ACAC\nBCBC
}


interface A {
    fun printA() {
    	println(&quot;AAAAA&quot;)
    }
    
    fun printC() {
    	println(&quot;ACAC&quot;)
    }
}

interface B {
	fun printB() {
    	println(&quot;BBBBB&quot;)
    }
    
    fun printC() {
    	println(&quot;BCBC&quot;)
    }
}

class Base: A, B {
	override fun printC() {
    	super&amp;lt;A&amp;gt;.printC()
        super&amp;lt;B&amp;gt;.printC()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;추상 Property&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1653603987048&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun main(args: Array&amp;lt;String&amp;gt;) {
	val s: Student = Student()
    println(s.age) // &quot;5&quot;
}

interface Person {
	val age: Int
    
    fun printAny() {
    	println(&quot;Person&quot;)
    }
}

class Student: Person {
	override val age: Int = 5
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Class&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1653604572699&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person(var name: String, val age: Int)

val p = Person(&quot;bin&quot;, &quot;29&quot;)
println(p.name) // &quot;bin&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var: getter / setter 이용 가능&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;val: getter만 이용 가능&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Data Class&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 있어서 Java를 이용할 때 Lombok에서의 Data 어노테이션 등등이 필요가 없어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;getter / setter 함수 생성&lt;/li&gt;
&lt;li&gt;hashCode(), equals(), toString() 생성&lt;/li&gt;
&lt;li&gt;copy(), componentN() 함수 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;copy(): 객체 복사&lt;/li&gt;
&lt;li&gt;componentN(): 객체가 가진 프로퍼티 개수만큼 호출 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;component1(): 객체의 첫 번째 프로퍼티 값 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린의 특성으로 인해 builder 어노테이션도 필요가 없어진다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Object Class&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에서는 직접 싱글턴 클래스를 만들어야 했지만, 코틀린에서는 object로 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출 시에 메모리에 적재하도록 Lazy 처리를 직접 하기도 했지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린의 object를 이용하면 해당 클래스는 프로세스 실행 시에 메모리에 올라가게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1653607518655&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;object class ExcelUtils {
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;용량이 큰 내부 변수들에 대해 &lt;b&gt;lazy 키워드&lt;/b&gt;로 처리하는 것으로 어느 정도 해결할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Enum Class&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티와 함수가 모두 타입이 동일해야 한다는 제약이 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1653605906477&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;enum class Error(val num: Int) {
	WARN(1) {
    	override fun getErrorName(): String {
        	return &quot;WARN&quot;
        }
    },
    
    ERROR(2) {
    	override fun getErrorName(): String {
        	return &quot;ERROR&quot;
        }
    },
    
    FAULT(3) {
    	override fun getErrorName(): String {
        	return &quot;FAULT&quot;
        }
    };
    
    abstract fun getErrorName(): String
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Sealed Class&lt;/b&gt;&lt;/h3&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;enum의 확장 형태이며, 클래스를 묶은 클래스다.&lt;/li&gt;
&lt;li&gt;1개 이상의 객체를 사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1653607121883&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sealed class Expr {
	data class Const(val number: Double): Expr()
    data class Sum(val e1: Expr, val e2: Expr): Expr()
    object NotANumber: Expr()
}

fun eval(expr: Expr): Double = when(expr) {
	is Const -&amp;gt; expr.number
    is Sum -&amp;gt; eval(expr.e1) + eval(expr.e2)
    NotANumber -&amp;gt; Double.NaN
    // 모든 케이스를 커버할 수 있기 때문에 else가 없어도 컴파일 타임 오류가 발생하지 않는다.
}


//....

val sum = Sum(Const(5.0), Const(8.0)) // 다양한 Const 객체 생성, Sum 객체 생성
val result = eval(sum)
println(result) // 13.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 보면 Enum과 다르게 다양한 프로퍼티를 가진 클래스를 정의하고,&amp;nbsp; 이용할 수 있다.&lt;/p&gt;</description>
      <category>Functional Programming</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/810</guid>
      <comments>https://teraphonia.tistory.com/810#entry810comment</comments>
      <pubDate>Fri, 27 May 2022 08:34:18 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin 함수 선언 방법</title>
      <link>https://teraphonia.tistory.com/809</link>
      <description>&lt;pre id=&quot;code_1653531336674&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun f1(value: Int): Int {
	return value * 3
}

fun f2(value: Int): Int = value * 2

fun f3(value: Int) = value * 2&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;명시하지 않으면 Unit(Void) 타입으로 설정된다.&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와 다르게 함수 파라미터 기본값을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 호출할 때 순서를 고려하지 않아도 된다. 다음과 같이 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1653531676158&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun sum(x: Int = 10, y: Int = 5): Int = x + y

println(sum(y = 3)) // 13
println(sum(y = 8, x = 6)) // 14&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;익명 함수, 람다 표현식&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다식을 이용해 익명 함수를 간결하게 표현한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653531828129&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun sum(x: Int, y: Int, calc: (Int, Int) -&amp;gt; Int): Int {
	return calc(x, y)
}

val value = sum(5, 10, { x, y -&amp;gt; x + y })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sum 함수 호출 시 넘겨준 람다식에서 x, y는 컴파일러가 타입 추론을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 kotlin은 함수에서 return을 사용하지 않으면 마지막 라인의 결과를 반환한다.&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 함수가 추가된 경우에, 이를 확장 함수라고 말한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653532043220&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun Int.multiply(value: Int): Int {
	return this * value
}

println(10.multiply(4)) // 40&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 모든 Int 타입에서 product 함수를 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도 인자 없이 this를 이용해 자신의 값에 접근이 가능하다.&lt;/p&gt;</description>
      <category>Functional Programming</category>
      <category>kotlin #함수형 프로그래밍</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/809</guid>
      <comments>https://teraphonia.tistory.com/809#entry809comment</comments>
      <pubDate>Thu, 26 May 2022 19:30:45 +0900</pubDate>
    </item>
    <item>
      <title>함수형 프로그래밍의 특징</title>
      <link>https://teraphonia.tistory.com/808</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;데이터 처리의 참조 투명성을 보장하며, 상태와 가변 데이터 생성을 피하는 프로그래밍 패러다임이다.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Nanum Gothic';&quot;&gt;특징&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;불변성(Immutable)&lt;/li&gt;
&lt;li&gt;참조 투명성(Referential Transparency)&lt;/li&gt;
&lt;li&gt;일급 함수(First Class Function)&lt;/li&gt;
&lt;li&gt;게으른 평가(Lazy Evaluation)&lt;/li&gt;
&lt;/ul&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;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동시성(Concurrency): 동시에 실행하는 것처럼 보이도록 하는 것&lt;/li&gt;
&lt;li&gt;병렬성(Parallel): 실제로 물리적으로 동시에 돌아가는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 복잡도가 낮아 간결한 코드 작성 가능&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;순수 함수(Pure Function)&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;y = f(x). 동일한 input은 동일한 output을 가짐.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상수가 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span&gt;전역 변수 등의&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;외부 변수&lt;/span&gt;&lt;/span&gt;를 이용하는 코드가 존재하면 순수 함수가 아니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;파일, 네트워크 I/O&lt;/li&gt;
&lt;li&gt;예외 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수 함수를 이용하게 되면 동시성 관련 코드를 작성할 때, 공유 자원이 변경될 걱정 없이 안전하게 코드를 작성할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;순수 함수가 아닌 함수의 특징&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Output에 대한 예측이 어렵다.&lt;/li&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;당연하게도 순수 함수가 아닌 함수를 이용하는 함수도 마찬가지로 순수 함수가 아니게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;참조 투명성: 표현식을 값으로 대체할 수 있는가?&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 + 1은 2로 대체될 수 있다.&lt;/li&gt;
&lt;li&gt;순수함수 y = f(x)가 주어졌을 때, f(x)가 y를 반환하므로 f(x)는 y로 대체 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;일급&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;일급 객체(First Class Object)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 3가지를 만족하는 객체를 뜻한다.&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;li&gt;객체를 변수, 자료구조에 담을 수 있다. ex) val anyList: List&amp;lt;Any&amp;gt; = listOf(Any())&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;일급 함수(First Class Function)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 3가지 조건에 객체 -&amp;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;ex) fun doSomething(func: (Int) -&amp;gt; String) { }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수를 함수 반환값으로 넘길 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) fun doSomething() : (Int) -&amp;gt; String { return { value -&amp;gt; value.toString() } }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수를 변수, 자료구조에 담을 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) var funcList : List&amp;lt;(Int) -&amp;gt; String&amp;gt; = listOf({ value -&amp;gt; value.toString() })&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;일급 함수를 이용한 추상화 / 재사용성 높이기&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;객체가 아닌, 함수를 DI할 수 있기 때문이다.&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;Lazy Evaluation으로 무한 자료구조 만들기&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1653526815816&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val lazyValue: String by lazy {
	println(&quot;taking long time&quot;)
    &quot;hello&quot;
}

fun main(args: Array&amp;lt;String&amp;gt;) {
	println(layValue)
	println(layValue)
}&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;taking long time&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hello&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;by lazy&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;값을 by lazy로 선언하고 매개변수로 람다 식을 넘기면 해당 인스턴스가 호출되는 시점에 람다 식이 한 번 실행된다.
&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;li&gt;값이 최초 한 번만 평가되기 때문에, 수정이 불가능하고, 그렇기 때문에 lazy는 var로 선언할 수 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;lazy를 이용하지 않는다면 &quot;taking long time&quot; 이 2번 평가된다.&lt;/li&gt;
&lt;/ul&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;명령형 언어에서는 일반적으로 무한대 값을 자료구조에 담을 수 없다. 있다면 무한 루프의 위험이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수형 언어에서는 Lazy Evaluation이라는 특성을 통해 이를 가능하게 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1653527248837&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val infValue = generateSequence(0) { it + 5 } // start from 0, then run this function. ex) f(f(f(0)))
infValue.take(5).forEach { 
	print(&quot;$it &quot;) 
}

// &quot;0 5 10 15 20&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;infValue는 generateSequence() 함수가 호출된 시점에 평가되지 않기 때문에(Lazy) 이러한 표현이 가능한 것이다.&lt;/p&gt;</description>
      <category>Functional Programming</category>
      <category>함수형 프로그래밍 #코틀린</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/808</guid>
      <comments>https://teraphonia.tistory.com/808#entry808comment</comments>
      <pubDate>Thu, 19 May 2022 07:50:55 +0900</pubDate>
    </item>
    <item>
      <title>Chrome이 Multi Thread가 아닌 Multi Process를 이용하는 이유</title>
      <link>https://teraphonia.tistory.com/807</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;장점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Process는 메모리를 공유하지 않기 때문에 메모리 공격에 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 서로 다른 사이트의 Frame / Tab을 서로 다른 렌더러가 담당하도록 해서 Same Origin Policy를 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Process는 죽어도 다른 프로세스가 죽지 않는다. 한 탭의 Renderer Process가 죽어도 다른 탭이 죽지 않는다.&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;1. 메모리 사용량 증가: Process는 메모리를 공유하지 않기 때문에 Process 별로 하나의 자바스크립트 엔진 코드를 들고 있어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. IPC: 프로세스간의 통신을 이용하는 코드가 많아지면서 코딩 난이도 상승. 그리고 송신-수신 이벤트를 받아서 처리하기 때문에 속도가 조금 느려진다.&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;</description>
      <category>팁</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/807</guid>
      <comments>https://teraphonia.tistory.com/807#entry807comment</comments>
      <pubDate>Tue, 15 Jun 2021 14:05:35 +0900</pubDate>
    </item>
    <item>
      <title>Annotations</title>
      <link>https://teraphonia.tistory.com/806</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;*JPA에서 Entity는 파라미터가 없는 생성자를 가져야 한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 220px;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;Lombok&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@NoArgsConstructor&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;파라미터가 없는 생성자를 만든다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 60px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 60px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@Data&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 60px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@Getter&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@Setter&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@ToString&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@EqualsAndHashCode를 붙인 효과와 같다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 40px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@RequiredArgsConstructor&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 40px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;final&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@NonNull&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;인 필드 값만 파라미터로 받는 생성자를 만든다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;JPA&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@Entity&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;DB 테이블 매칭되는 클래스로 지정&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 270px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@PrePersist&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@Entity 저장 전에 호출&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Javax Validation&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;NotBlank&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;NULL, &quot;&quot;, &quot; &quot; 모두 비허용&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;@Pattern&lt;span style=&quot;color: #333333;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Regexp 형태 체크&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;@Digits&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;정수값 형태 체크&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 270px; text-align: center; height: 20px;&quot;&gt;Hibernate Validator&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;@CreditCardNumber&lt;/td&gt;
&lt;td style=&quot;width: 269px; text-align: center; height: 20px;&quot;&gt;Credit Card 번호 형태 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Web/Spring</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/806</guid>
      <comments>https://teraphonia.tistory.com/806#entry806comment</comments>
      <pubDate>Thu, 25 Mar 2021 19:18:36 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] mysql 설치 및 실행</title>
      <link>https://teraphonia.tistory.com/805</link>
      <description>&lt;p&gt;1. docker pull mysql&lt;/p&gt;
&lt;p&gt;- docker pull mysql:{version}&lt;/p&gt;
&lt;p&gt;2. run mysql image&lt;/p&gt;
&lt;pre id=&quot;code_1616662920864&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=&amp;lt;password&amp;gt; --name mysql mysql:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3. connect to mysql&lt;/p&gt;
&lt;pre id=&quot;code_1616663040036&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker exec -it mysql bash
mysql -u root -p&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>docker mysql</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/805</guid>
      <comments>https://teraphonia.tistory.com/805#entry805comment</comments>
      <pubDate>Thu, 25 Mar 2021 18:04:09 +0900</pubDate>
    </item>
    <item>
      <title>[Docker] Windows에 설치 시 Hardware assisted visualization... 에러 발생 해결</title>
      <link>https://teraphonia.tistory.com/804</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQBxRd/btq039vftMV/hLKQBSohc2e914ygWCQbO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQBxRd/btq039vftMV/hLKQBSohc2e914ygWCQbO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQBxRd/btq039vftMV/hLKQBSohc2e914ygWCQbO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQBxRd%2Fbtq039vftMV%2FhLKQBSohc2e914ygWCQbO0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;1. PowerShell을 관리자 권한으로 실행&lt;/p&gt;
&lt;p&gt;2. &lt;span&gt;dism.exe /Online /Enable-Feature:Microsoft-Hyper-V /All&amp;nbsp;&lt;br /&gt;&amp;nbsp; 입력&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;3. Windows 재시작&lt;/span&gt;&lt;/p&gt;</description>
      <category>Docker</category>
      <category>docker windows</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/804</guid>
      <comments>https://teraphonia.tistory.com/804#entry804comment</comments>
      <pubDate>Thu, 25 Mar 2021 17:45:41 +0900</pubDate>
    </item>
    <item>
      <title>Context Switching에서의 Thread와 Process의 관계 + PCB, TCB</title>
      <link>https://teraphonia.tistory.com/802</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Process란?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- PC와 Register Set, Process ID, State, 메모리 등의 정보를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 프로세스는 적어도 하나의 Thread를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Thread란?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- CPU 스케쥴링의 기본 단위.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Program Counter, Register들과 Stack으로 구성&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- Process 내부에서 여러 Thread 생성 가능하며, 코드와 메모리를 공유한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #000000;&quot;&gt;운영체제 책에는 이렇게 설명되어 있지만, 의문이 든다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&quot;프로세스가 여러개의 스레드를 가지면 PCB 하나로 어떻게 저장하지? 각 Thread의 정보를 저장해야 할 텐데.&quot;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;이렇게 생각해서 알아본 것이 TCB이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;PCB&lt;/b&gt;는 Process ID와 상태, 우선순위, 메모리 정보 등을 저장한다. 멀티스레드가 아닌 멀티프로세스 환경에서는 PCB가 PC와 Register Set 정보도 포함한다. 여기서는 멀티스레드 환경이라 가정한다.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;b&gt;TCB&lt;/b&gt;는 Thread별로 존재하는 자료구조이며, PC와 Register Set(CPU 정보), 그리고 PCB를 가리키는 포인터를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;591&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sxO0J/btqEwQ5PbRD/krWKDTE60qcaJpksIFcAy1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sxO0J/btqEwQ5PbRD/krWKDTE60qcaJpksIFcAy1/img.jpg&quot; data-alt=&quot;TCB는 PCB를 가리키는 포인터를 가진다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sxO0J/btqEwQ5PbRD/krWKDTE60qcaJpksIFcAy1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsxO0J%2FbtqEwQ5PbRD%2FkrWKDTE60qcaJpksIFcAy1%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;591&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;TCB는 PCB를 가리키는 포인터를 가진다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그리고 TCB는 PCB보다 적은 데이터를 가지는 자료구조이다. 해당 Thread에 대한 정보만 저장하면 되기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;592&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U3jIY/btqEwH85gqM/skmJtaYqq1MWeyUght7aT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U3jIY/btqEwH85gqM/skmJtaYqq1MWeyUght7aT0/img.jpg&quot; data-alt=&quot;TCB가 PCB보다 데이터가 적다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U3jIY/btqEwH85gqM/skmJtaYqq1MWeyUght7aT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU3jIY%2FbtqEwH85gqM%2FskmJtaYqq1MWeyUght7aT0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;592&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;TCB가 PCB보다 데이터가 적다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;보통 TCB는 커널 레벨에서 Context Switching의 기본 단위가 되며, 같은 프로세스에서의 스위칭에 대해서는 TCB 정보만 저장하면 된다.&lt;/p&gt;
&lt;p&gt;하지만 다른 프로세스 간의 스위칭을 할 때에는 PCB / TCB 정보를 모두 저장해야 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;611&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zGijd/btqEwhpfF0R/uvjAorkLXwqTXG1j7JE9K0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zGijd/btqEwhpfF0R/uvjAorkLXwqTXG1j7JE9K0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zGijd/btqEwhpfF0R/uvjAorkLXwqTXG1j7JE9K0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzGijd%2FbtqEwhpfF0R%2FuvjAorkLXwqTXG1j7JE9K0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;611&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;User Level Thread&lt;/h2&gt;
&lt;p&gt;- Kernel에서 관리되지 않는 Thread&lt;/p&gt;
&lt;p&gt;- 커널에서 보기에 프로세스와 스레드 비율 1:1인 프로그램이므로, Time slice를 받게 되면 User Level에서 분할하여 사용하게 된다.&lt;/p&gt;
&lt;p&gt;- 스케줄러도 User Level(Runtime/RAM)에 존재하며, Context Switching을 진행할 때 TCB 정보만 저장/복구하면 되기 때문에 빠르다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;- System Call이 발생하면 프로세스 자체가 Block된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;- Interrupt 발생(I/O작업 등)이 없는 프로그램이라면 이 방식이 유리하다.&lt;/p&gt;
&lt;p&gt;- 운영체제에 따른 영향을 덜 받기 때문에 이식성이 좋다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Kernel Level Thread&lt;/h2&gt;
&lt;p&gt;- Kernel에서 관리되는 Thread&lt;/p&gt;
&lt;p&gt;- System Call이 발생해도 해당 Thread만 Block되어 wait_queue에 들어간다.&lt;/p&gt;
&lt;p&gt;- 운영체제에 따라 정책이 다르기 때문에, 이식성이 낮다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;*추가정보 : JVM, V8...&lt;/h2&gt;
&lt;p&gt;JVM같은 경우 User Level Thread를 이용하며, 내부적으로는 Kernel Level Thread로 1:1매핑한다.&lt;/p&gt;
&lt;p&gt;Node.js의 엔진인 V8 같은 경우, 내부적으로 C++을 사용하는데, 리눅스에서는 C++ 에서 Thread를 이용할 때 User / Kernel Level Thread를 1:1매칭하는 pThread 라이브러리를 이용한다.&lt;/p&gt;
&lt;p&gt;게다가 V8은 내부적으로 IOCP를 이용하여 비동기 IO를 구현하므로 IO 관련 System Call에 대해서는 Block되지 않는다.&lt;/p&gt;
&lt;p&gt;그렇기 때문에 Spring 서버와 Node.js 서버의 차이는 다음과 같다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node.js&lt;/h3&gt;
&lt;p&gt;- IOCP를 통해 비동기 I/O를 이용하므로, Block되지 않는다.&lt;/p&gt;
&lt;p&gt;- 동기화 작업을 수행할 필요가 없지만 I/O를 제외한 CPU 작업을 수행하게 되면 그동안 Event Loop가 블락되기 때문에 조심해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Spring&lt;/h3&gt;
&lt;p&gt;- 멀티스레드로 I/O를 관리하면 Block되지 않는다. 예를 들면 Network I/O 등이 있다. Spring은 요청 당 하나의 스레드를 할당하여 Block되지 않도록 한다. 하지만 이 때 요청 수가 스레드 수보다 많다면 처리중인 요청이 모두 완료될 때까지 대기하게 된다.&lt;/p&gt;
&lt;p&gt;- 동기화 처리를 제대로 하지 못한다면 문제가 발생한다.&lt;/p&gt;
&lt;p&gt;- CPU 작업을 다른 스레드에서 진행할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이미지 출처&lt;/p&gt;
&lt;p&gt;- &lt;a href=&quot;https://slideplayer.com/slide/12698810/&quot;&gt;https://slideplayer.com/slide/12698810/&lt;/a&gt;&lt;/p&gt;</description>
      <category>OS</category>
      <category>OS</category>
      <category>PCB</category>
      <category>Process</category>
      <category>TCB</category>
      <category>thread</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/802</guid>
      <comments>https://teraphonia.tistory.com/802#entry802comment</comments>
      <pubDate>Sat, 30 May 2020 03:13:37 +0900</pubDate>
    </item>
    <item>
      <title>컴퓨터 자원의 구조와 동작, RAM과 Disk에 대해</title>
      <link>https://teraphonia.tistory.com/801</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;구조도&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Compure Structure.jpg&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;564&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mPEmX/btqEp01NrXR/IrDYsYxM7OKjggwg0UYz11/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mPEmX/btqEp01NrXR/IrDYsYxM7OKjggwg0UYz11/img.jpg&quot; data-alt=&quot;구조도&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mPEmX/btqEp01NrXR/IrDYsYxM7OKjggwg0UYz11/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmPEmX%2FbtqEp01NrXR%2FIrDYsYxM7OKjggwg0UYz11%2Fimg.jpg&quot; data-filename=&quot;Compure Structure.jpg&quot; data-origin-width=&quot;1083&quot; data-origin-height=&quot;564&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;구조도&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CPU와 I/O Controller의 관계&lt;/h2&gt;
&lt;p&gt;각각의 I/O Controller는 CPU와 독립적으로 그 기능을 수행하며, Bus라는 통로와 Interrupt를 통해 CPU와 통신한다.&lt;/p&gt;
&lt;p&gt;프로그래밍을 할 때, 흔히 I/O 작업 수행에 대한 이야기를 한다.&lt;/p&gt;
&lt;p&gt;I/O 작업의 의미는 CPU가 I/O Controller의 Data Buffer에 데이터를 채운 후 Write 명령을 수행하는 것이다.&lt;/p&gt;
&lt;p&gt;I/O작업이 수행되면 각 I/O Controller는 독립적으로 그 기능을 수행하며, 작업이 완료되면 Interrupt를 발생시켜 최종적으로는 CPU에게 작업이 완료되었음을 알린다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RAM과 Disk&lt;/h2&gt;
&lt;p&gt;RAM은 &lt;span style=&quot;color: #333333;&quot;&gt;CPU가 직접 접근할 수 있는 비휘발성 메모리이며, &lt;/span&gt;Random Access Memory이다.&lt;/p&gt;
&lt;p&gt;RAM의 역할은 하드디스크로부터 일정량의 데이터를 복사해서 임시로 저장한 후에 필요할 때마다 CPU에 빠르게 전달하는 역할을 한다.&lt;/p&gt;
&lt;p&gt;컴퓨터에 쓰이는 부품들은 이렇게 각각의 역할을 기반으로 만들어진다는 것을 유의해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 RAM과 DISK를 왜 구분하는 것인지 의문이 들 것이다. DISK를 RAM 대신 쓰면 안되는 것일까?&lt;/p&gt;
&lt;p&gt;이는 전력 소비에 대한 효율성과 속도 때문에 그렇게 하지 않는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;흔히 RAM을 휘발성 메모리라 한다. 휘발성 메모리라 함은 '지속적으로 전력 공급을 해야 하는 메모리' 이며, 전원 공급이 없으면 메모리를 유지할 수 없다는 의미이다.&lt;/p&gt;
&lt;p&gt;DISK는 비휘발성 메모리이며, 지속적인 전력 공급이 필요하지 않으며, 전원 공급이 없어도 메모리를 유지한다는 의미이다.&lt;/p&gt;
&lt;p&gt;그렇기 때문에 '비휘발성 메모리'라는 것은 전력을 많이 사용하며, 결과적으로 메모리 접근 속도가 빠르도록 설계되었다고 볼 수 있는데, 핵심적인 것은 RAM은 이름 그대로 Random Access가 된다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Random Access가 의미하는 것은 특정 메모리 주소를 통해 바로 접근이 가능하다는 의미인데, 하드&amp;nbsp;&lt;/span&gt;Disk는 Random Access 방식이 아니고 데이터가 저장된 위치, 디스크의 구조에 따라 해당 메모리에 접근하는 속도가 모두 다르다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요약하자면 RAM과 Disk는 역할이 다르게 설계되었으며,&lt;/p&gt;
&lt;p&gt;RAM은 Random Access가 가능하여 빠른 메모리이지만, 지속적으로 전력 공급이 되지 않으면 데이터가 날아가는 장치,&lt;/p&gt;
&lt;p&gt;Disk는 Random Access가 불가능하여 느린 메모리이지만, 지속적으로 전력 공급이 되지 않아도 데이터가 날아가지 않는 장치이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;설계 목적을 기반으로 말해 보자면, CPU는 자체 캐시의 크기가 작으며, 직접 접근할 수 있는 대용량 메모리가 필요했다.&lt;/p&gt;
&lt;p&gt;Disk는 느리지만 결과적으로 CPU가 Disk의 데이터를 사용해야 하기 때문에 중간에 더 빠른 메모리 장치가 있다면 더 효율적인 처리가 가능해진다는 생각으로 RAM이 만들어졌다고 보면 될 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;틀린 점이 있다면 댓글을 달아주시면 감사하겠습니다.&lt;/p&gt;</description>
      <category>OS</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/801</guid>
      <comments>https://teraphonia.tistory.com/801#entry801comment</comments>
      <pubDate>Tue, 26 May 2020 03:02:56 +0900</pubDate>
    </item>
    <item>
      <title>V8 Engine의 Garbage Collection</title>
      <link>https://teraphonia.tistory.com/800</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;목적&lt;/h2&gt;
&lt;p&gt;GC의 목적 자체는 단순하다. 사용되지 않는 메모리를 찾아 재사용할 수 있는 메모리로 전환하는 것이다.&lt;/p&gt;
&lt;p&gt;GC는 Heap 메모리를 대상으로 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Root Object&lt;/h2&gt;
&lt;p&gt;- V8 엔진에서 직접 참조되는 객체&lt;/p&gt;
&lt;p&gt;- 전역 객체, Browser에서는 DOM 등&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;살아 있다? - Live 객체&lt;/h2&gt;
&lt;p&gt;- Pointer의 체이닝을 탐색해 접근 가능하다&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stack: 함수 단위로 이루어지며,&amp;nbsp;지역 변수, 실행 Parameter, 반환값, 반환 주소, 객체의 포인터 주소가 저장됨&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Heap: 객체를 대상으로 함&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Heap의 구조&lt;/h2&gt;
&lt;p&gt;를 그림으로 살펴 보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Node.js Heap2.jpg&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;911&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dg8svk/btqEoVr7PPT/7UH4EaQDkUzSQgxIFfzOjk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dg8svk/btqEoVr7PPT/7UH4EaQDkUzSQgxIFfzOjk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dg8svk/btqEoVr7PPT/7UH4EaQDkUzSQgxIFfzOjk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdg8svk%2FbtqEoVr7PPT%2F7UH4EaQDkUzSQgxIFfzOjk%2Fimg.jpg&quot; data-filename=&quot;Node.js Heap2.jpg&quot; data-origin-width=&quot;711&quot; data-origin-height=&quot;911&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Pointer 탐색&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Remember Set&lt;/h3&gt;
&lt;p&gt;먼저, 힙 영역에서 Pointer와 Data를 구분해야 한다.&lt;/p&gt;
&lt;p&gt;GC Root로부터 포인터를 탐색하여 Live 객체를 찾는다.&lt;/p&gt;
&lt;p&gt;Java와 같은 정적 타입 언어일 경우, 컴파일 단계에서 각 Class 내의 메모리 Offset을 미리 알 수 있어, 어떤 객체에 대해 미리 파악한 Offset을 통해 모든 포인터를 찾아낼 수 있다.&lt;/p&gt;
&lt;p&gt;JS는 동적 타입 언어이기 때문에 프로그램을 실행하며 실제로 코드가 컴파일 될 때 해당 객체들의 Offset을 매번 Update하여 이를 기록해 둔다.&lt;/p&gt;
&lt;p&gt;GC 과정에서 객체가 Heap에서 이동할 때, 해당 메모리의 &lt;span style=&quot;color: #333333;&quot;&gt;Pointer&lt;/span&gt;들을 갱신해 주어야 한다. 이 작업을 위해 이전 위치에 대한 Pointer들을 찾아야 하는데, V8 엔진에서는 이를 위해 Remember Set이라는 자료구조를 사용하여 이 포인터들을 기록해 추적한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Garbage Collection&lt;/h2&gt;
&lt;p&gt;일반적으로 대부분의 Application에서, 객체들은 수명이 짧은 경우가 많다.&lt;/p&gt;
&lt;p&gt;이런 특성을 캐치해서 Heap을 New Space / Old Space의 2개의 영역으로 나눈다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;660px;&quot; height=&quot;330px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4V1Fb/btqEmMpU1St/2KITdrIi4bnikekwYotKq0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4V1Fb/btqEmMpU1St/2KITdrIi4bnikekwYotKq0/img.jpg&quot; data-alt=&quot;간략한 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4V1Fb/btqEmMpU1St/2KITdrIi4bnikekwYotKq0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4V1Fb%2FbtqEmMpU1St%2F2KITdrIi4bnikekwYotKq0%2Fimg.jpg&quot; width=&quot;660px;&quot; height=&quot;330px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;간략한 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;New Space&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;New Space는 1~8MB(작은 크기)로 설정이 가능하며, &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 이상의 크기를 가지는 객체는 Large Object Space에 저장된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 공간은 From / To Space로 2개의 영역(역할)으로 나눠지며, 객체들은 항상 To Space에 할당되는데, 객체에 대한 메모리를 할당할 때마다 증가하는 할당 포인터가 존재하고 이 값이 해당 Space의 끝에 도달하게 되면 Minor GC(Scavenge)가 발생하게 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Minor GC&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;To-Space에 남은 메모리보다 큰, 새로운 객체를 메모리에 할당하는 상황이라고 가정하자.&lt;/p&gt;
&lt;p&gt;먼저, &lt;span style=&quot;color: #333333;&quot;&gt;From Space와 To Space의 역할이 바뀐다.&lt;/span&gt; 이 때 살아남은 객체들은 To Space로 이동하게 되며, 연속된 메모리로 구성하여 메모리 단편화를 해결해 메모리를 추가적으로 확보한다. 메모리가 확보되었다면 이 때 새로운 객체를 To-Space에 할당한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;572px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vAhNP/btqElXTb3lp/U0SXYHiKBbXyiiYV4pFRE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vAhNP/btqElXTb3lp/U0SXYHiKBbXyiiYV4pFRE1/img.png&quot; data-alt=&quot;새로운 객체 할당하며 Minor GC 발생&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vAhNP/btqElXTb3lp/U0SXYHiKBbXyiiYV4pFRE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvAhNP%2FbtqElXTb3lp%2FU0SXYHiKBbXyiiYV4pFRE1%2Fimg.png&quot; width=&quot;572px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;새로운 객체 할당하며 Minor GC 발생&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;572px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwVB78/btqEozQqKDk/LzxCHzpbtEdzhK7OCfSUeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwVB78/btqEozQqKDk/LzxCHzpbtEdzhK7OCfSUeK/img.png&quot; data-alt=&quot;역할 변경 후 메모리 단편화 해결하며 To Space로 살아남은 객체 이동 후 From Space의 Garbage 제거&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwVB78/btqEozQqKDk/LzxCHzpbtEdzhK7OCfSUeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwVB78%2FbtqEozQqKDk%2FLzxCHzpbtEdzhK7OCfSUeK%2Fimg.png&quot; width=&quot;572px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;역할 변경 후 메모리 단편화 해결하며 To Space로 살아남은 객체 이동 후 From Space의 Garbage 제거&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;577&quot; height=&quot;NaN&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRTfks/btqEoA2Rjxe/QCthw59WKcXN1Ap0Mx63t0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRTfks/btqEoA2Rjxe/QCthw59WKcXN1Ap0Mx63t0/img.png&quot; data-alt=&quot;Minor GC가 끝난 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRTfks/btqEoA2Rjxe/QCthw59WKcXN1Ap0Mx63t0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRTfks%2FbtqEoA2Rjxe%2FQCthw59WKcXN1Ap0Mx63t0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;577&quot; height=&quot;NaN&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;/&gt;&lt;/span&gt;&lt;figcaption&gt;Minor GC가 끝난 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;시간이 흘러, To Space에 객체가 많이 생겨 또 메모리가 부족하게 되었고, &lt;span style=&quot;color: #333333;&quot;&gt;새로운 객체를 할당하려고 하는 상황이라&lt;/span&gt; 가정한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-05-24 오후 8.12.55.png&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1318&quot; width=&quot;570&quot; height=&quot;NaN&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IdQsI/btqEm3dSTnC/DBKHgpbHa12GVNaKggvVTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IdQsI/btqEm3dSTnC/DBKHgpbHa12GVNaKggvVTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IdQsI/btqEm3dSTnC/DBKHgpbHa12GVNaKggvVTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIdQsI%2FbtqEm3dSTnC%2FDBKHgpbHa12GVNaKggvVTK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-05-24 오후 8.12.55.png&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1318&quot; width=&quot;570&quot; height=&quot;NaN&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;마찬가지로 To / From Space의 역할을 바꾸고, From Space에서 살아남은 객체들을 To-Space로 옮기며 연속된 메모리로 할당하여 메모리 단편화를 해결하여 추가적인 메모리를 확보한다. 이 때, GC에서 2번 이상 살아남게 된 객체들은 Old Space로 이동한다.&lt;/p&gt;
&lt;p&gt;Old Space에 저장될 때, 다른 객체를 참조하는 포인터를 가진 객체는 Pointer Space에, Raw Data만을 가진 객체들은 Data Space에 위치시킨다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;573px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1FhCC/btqEoVyWXMy/grRwtrH1ZQK4fgOkRw3YTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1FhCC/btqEoVyWXMy/grRwtrH1ZQK4fgOkRw3YTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1FhCC/btqEoVyWXMy/grRwtrH1ZQK4fgOkRw3YTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1FhCC%2FbtqEoVyWXMy%2FgrRwtrH1ZQK4fgOkRw3YTk%2Fimg.png&quot; width=&quot;573px;&quot; height=&quot;311px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;b&gt;&lt;/b&gt;&lt;/b&gt;이후, From-Space에 남은 객체들을 Garbage로 간주하여 삭제하고 To-Space에 새로운 객체를 할당한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;601&quot; height=&quot;NaN&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y6JKP/btqEm2zgd9t/v4570hRAzCq9l8XacWL2O1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y6JKP/btqEm2zgd9t/v4570hRAzCq9l8XacWL2O1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y6JKP/btqEm2zgd9t/v4570hRAzCq9l8XacWL2O1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy6JKP%2FbtqEm2zgd9t%2Fv4570hRAzCq9l8XacWL2O1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;601&quot; height=&quot;NaN&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 과정을&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;V8의 Minor GC라 하며,&lt;/span&gt; 사용된 알고리즘을 Scavenge라 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Scavenge 알고리즘은 빠른 성능을 보이지만, To/From Space에 대해 물리적인 메모리 백업이 필요하므로 메모리 공간에 대한 오버헤드가 존재한다. 그래서 Old Space같이 큰 메모리 영역에 대해서는 Mark-Sweep-Compact 알고리즘을 이용한 방식인 Major GC를 진행해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V8의 Minor GC 처리: Parallel Scavenging&lt;/h3&gt;
&lt;p&gt;STW를 발생시키며 다른 Worker 스레드들과 함께 위의 알고리즘을 처리한다.&lt;/p&gt;
&lt;p&gt;다른 스레드가 동일한 객체에 대해 작업을 수행하지 않도록 동기화 작업을 수행하는 오버헤드가 있다.&lt;/p&gt;
&lt;p&gt;이 때 살아남은 객체들이 To-Space로 이동하게 되며, 모든 스레드들은 이동된 객체에 대한 포인터들을 갱신하는데, 이 때 다른 스레드들이 이 객체에 접근했을 경우 잘못된 메모리를 가리키는 포인터로 파악할 수 있다. 이를 알 수 있도록 하기 위해 해당 메모리에 Fowarding pointer(해당 객체에 대한 새로운 메모리 주소를 가리키는 포인터)를 남긴다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ea4vRI/btqEniouF3A/fDBvj7mVCwckIxrWs3j081/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ea4vRI/btqEniouF3A/fDBvj7mVCwckIxrWs3j081/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ea4vRI/btqEniouF3A/fDBvj7mVCwckIxrWs3j081/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fea4vRI%2FbtqEniouF3A%2FfDBvj7mVCwckIxrWs3j081%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Major GC&lt;/h3&gt;
&lt;p&gt;- Old Space가 꽉 차게 될 때 발생&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Marking&lt;/h4&gt;
&lt;p&gt;다음의 3가지 상태로 표현된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;White: 아직 GC가 탐색하지 못한 상태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Gray: GC가 탐색했으나, 해당 객체가 참조하는 객체들에 대해서는 탐색하지 못한 상태&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Black: GC가 탐색했으며, 해당 객체가 참조하는 객체들에 대해서도 탐색이 완료된 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이는 기본적으로 DFS로 수행되며, Deque를 이용하고 탐색에서는 Stack 형태로 사용한다.&lt;/p&gt;
&lt;p&gt;처음에 모든 객체는 흰색으로 마킹되어 있다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;먼저, Root 객체를 회색으로 마킹한 후 Marking Deque에&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;Push하는 것으로 시작한다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;이후, 다음 과정을 반복한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;1. Marking Deque에서 Pop_front()를 하여 객체를 꺼낸다. &lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;2. 꺼낸 객체를 검은색으로 마킹한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;3. 인접 객체를 회색으로 마킹한 후, Marking Deque에 Push_front()를 한다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;* 여러 곳에서 참조하는 객체가 존재한다면 이미 회색이거나 검은색인 객체가 있을 수 있다. 이 객체들에 대해서는 처리하지 않는다. 즉, 흰색 객체가 회색 객체로 바뀐 경우에만 Push를 한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DFS 알고리즘이므로 이 Deque가 비어 있게 되면 종료되며, 살아 있는 모든 객체들은 검은색으로 마킹된 상태가 되고 죽어 있는 객체들은 흰색으로 마킹된 상태가 된다.&lt;/p&gt;
&lt;p&gt;Queue에 오버플로우가 발생한 경우, 회색 객체들이 남지만 Deque가 빈 상태가 되어 문제가 발생한다. 이 때에는 Heap을 탐색하여 회색 마킹된 오브젝트들을 찾아 Deque에 다시 넣어주고 탐색 알고리즘을 다시 수행한다.&lt;/p&gt;
&lt;p&gt;Marking이 완료되면 Sweep 알고리즘이 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;만약 검은색 마킹이 없다면 어떨까?&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;Pop된 객체는 회색이며, 이 때 별다른 처리가 없다고 가정해도 &lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;로직을 끝낼 수 있지만, 특정 객체에 대한 탐색이 완전히 끝났다는 정보를 어딘가에 기록해야 한다. 이를 위한 것이 검은색으로 마킹하는 것이다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Sweep&lt;/h4&gt;
&lt;p&gt;Page들은 Free List와 Marking Bitmap을 가지고 있는데,&lt;/p&gt;
&lt;p&gt;이 프로세스는 &lt;span style=&quot;color: #333333;&quot;&gt;Page 단위로 수행되며,&lt;/span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;Marking Bitmap을 탐색해 흰색 객체(&lt;span style=&quot;color: #333333;&quot;&gt;죽어 있는 객체)&lt;/span&gt;를 찾아&lt;/span&gt; 탐색하여 Free Space로 전환시키고 Free List에 해당 메모리를 추가한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Compact&lt;/h4&gt;
&lt;p&gt;메모리 단편화가 심한 Page의 객체들을 공간이 여유로운 Page 혹은 새로운 Page에 이동시키면서 재배치를 하여 추가적인 메모리를 확보한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;V8은 Page를 비우는 과정에서 옮겨진 객체들에 대한 포인터들을 기록해두며, Page가 비워지게 되면 이들을 옮긴 객체들을 가리키도록 업데이트한다.&lt;/p&gt;
&lt;p&gt;다음 그림은 전체적인 과정을 간략하게 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;574px;&quot; height=&quot;322px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEkhNs/btqEoAhx6q0/x57jk6pga9KpkllY3ysHyk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEkhNs/btqEoAhx6q0/x57jk6pga9KpkllY3ysHyk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEkhNs/btqEoAhx6q0/x57jk6pga9KpkllY3ysHyk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bEkhNs/btqEoAhx6q0/x57jk6pga9KpkllY3ysHyk/img.gif&quot; width=&quot;574px;&quot; height=&quot;322px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Write Barrier&lt;/h3&gt;
&lt;p&gt;만약, Old Space의 객체가 New Space의 객체를 참조하고 있는 상황이라 가정해 보자.&lt;/p&gt;
&lt;p&gt;Minor GC가 발생하여 New Space의 객체가 삭제되게 되면, 해당 객체를 참조하고 있는 Old GC의 객체는 삭제된 메모리의 객체를 참조하는 문제가 발생하게 된다.&lt;/p&gt;
&lt;p&gt;그렇다고 이를 위해 Heap 전체에 대한 탐색을 진행한다면 이에 대한 오버헤드가 커지게 된다.&lt;/p&gt;
&lt;p&gt;그렇기 때문에 어플리케이션에서 참조를 대입하거나 수정할 때, 해당 객체에 대해 '어떤 영역의 객체가 어떤 영역의 객체를 참조한다'는 정보를 기록해 두는 방법을 사용하게 되었고, 이를 Write Barrier라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;406px;&quot; height=&quot;419px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b92Ju8/btqEmLYRpTM/FNqcPnLv62284WgOCK9jI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b92Ju8/btqEmLYRpTM/FNqcPnLv62284WgOCK9jI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b92Ju8/btqEmLYRpTM/FNqcPnLv62284WgOCK9jI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb92Ju8%2FbtqEmLYRpTM%2FFNqcPnLv62284WgOCK9jI0%2Fimg.png&quot; width=&quot;406px;&quot; height=&quot;419px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Mark-Sweep-Compaction을 어떻게 처리해야 효율적일까?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;GC는 위에서 설명한 원리를 바탕으로 동작하지만, &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;모든 Minor/Major GC는 Stop the world(GC를 제외한 모든 어플리케이션의 스레드가 멈추는 현상)가 발생하며, 이를 어떻게 처리하는지에 따라 그 시간을 줄일 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이에 대해 V8에서 어떻게 처리하고 있는지에 대해 알아본다. &lt;span style=&quot;color: #333333;&quot;&gt;Minor GC는 수행시간이 빨라 Major GC에 대해서만 다룬다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;V8에서는 Parrallel Marking과 Concurrent Marking 기법을 합쳐서 이용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;직접 이를 구현한다고 가정해 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;먼저, 하나의 메인 스레드에서 이를 구현한다면 다음과 같은 형태가 될 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Single Thread(on Main Thread) GC&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjAjN0/btqEmoJO4K0/fhKGaLeV233xmS5KifgMNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjAjN0/btqEmoJO4K0/fhKGaLeV233xmS5KifgMNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjAjN0/btqEmoJO4K0/fhKGaLeV233xmS5KifgMNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjAjN0%2FbtqEmoJO4K0%2FfhKGaLeV233xmS5KifgMNk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;STW 시간이 엄청나게 길어지게 된다. 이는 어플리케이션이 정상적으로 동작하지 않는 형태가 된다.&lt;/p&gt;
&lt;p&gt;&quot;GC작업을 잘개 쪼개서 수행하면 어떨까?&quot; 하는 생각으로 만들게 된 것이 Incremental GC이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Incremental GC&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bce0cI/btqEmL5KRrB/9AALxk1H9c3M64zX24dlZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bce0cI/btqEmL5KRrB/9AALxk1H9c3M64zX24dlZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bce0cI/btqEmL5KRrB/9AALxk1H9c3M64zX24dlZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbce0cI%2FbtqEmL5KRrB%2F9AALxk1H9c3M64zX24dlZK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;간단하다고 생각할 수 있지만, 이는 Heap 객체 그래프가 GC가 완료되기 전에 수정될 수 있다는 문제가 존재한다.&lt;/p&gt;
&lt;p&gt;이를 막기 위해서는 검은색 마킹된 객체가 흰색 마킹된 객체를 참조하게 되지 않아야 한다는 것이다.&lt;/p&gt;
&lt;p&gt;GC가 잘 동작하려면 검은색 마킹된 객체의 인접 객체들은 흰색이 아니어야 하기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 이 때, Write Barrier를 이용한 처리를 진행한다.&lt;/p&gt;
&lt;p&gt;객체가 어떤 객체를 참조할 때, Write Barrier를 통해 Old -&amp;gt; New 포인터로의 정보를 기록하는 것 뿐만 아니라 검은색 객체가 흰색 객체를 참조하는 것도 기록한다.&lt;/p&gt;
&lt;p&gt;Marking을 부분적으로 진행하면서 검은색 -&amp;gt; 흰색 참조를 하는 객체를 발견하면, 처리하지 않고 회색으로 변경하여 다시 Marking Worklist에 추가한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 잘 동작하는 것을 보게 되면 자연스레 멀티스레드를 이용한 방식을 떠올리게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parrallel Marking&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Marking 단계부터 &lt;/span&gt;간단하게 생각해 보면 어떻게든 멀티스레딩을 이용하여 수행하면 성능이 좋아질 것 같다는 생각이 든다.&lt;/p&gt;
&lt;p&gt;Stop the world 없이 멀티스레드로 마킹을 진행한다면 어떨까? 하지만 문제점이 떠오를 것이다.&lt;/p&gt;
&lt;p&gt;&quot;Marking을 하는 도중에 Heap 그래프가 바뀐다면, Marking했던 정보들이 쓸모가 없어지는데..&quot;&lt;/p&gt;
&lt;p&gt;그렇다면 STW를 그대로 둔다고 가정한다면 위와 같은 문제를 고려하지 않아도 된다. 결과적으로 GC 속도를 빠르게 하는 것이 목표라면?&lt;/p&gt;
&lt;p&gt;이에 대한 GC 자료구조를 Thread-Safe하게 만들면 된다.&lt;/p&gt;
&lt;p&gt;즉, 스레드 간의 마킹 결과를 효율적으로 공유할 방법을 생각해 봐야 한다. 다음처럼 구조를 만들면 어떨까?&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Marking Worklist : 위에서 설명한 Major GC의 Marking Queue&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uEt7S/btqEoBU618Z/M0RIHhUy1yoqVCqsz7iXVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uEt7S/btqEoBU618Z/M0RIHhUy1yoqVCqsz7iXVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uEt7S/btqEoBU618Z/M0RIHhUy1yoqVCqsz7iXVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuEt7S%2FbtqEoBU618Z%2FM0RIHhUy1yoqVCqsz7iXVK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Worker 스레드들은 Heap 객체 그래프를 Read하기만 하며, Marking Worklist와 &lt;span style=&quot;color: #333333;&quot;&gt;Page의 Marking bitmap에 대해서만 Read/Write가 가능하도록 구성하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;전역 Marking Worklist에 대해서는 Read/Write 락을 통해 동기화하며, 이를&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;Worker 스레드들이 부분적으로 가져가 각자 가지고 있는 Marking worklist에 넣고 처리한다.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Marking bitmap은 atomic 계산으로만 수정되므로 이에 대한 lock은 필요하지 않으며, 이 값을 통해 각 스레드에서 객체의 색을 구분해 처리한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약 어떤 Worker Thread의 Marking Worklist에 객체가 너무 많다면 이를 전역 Marking Worklist에 보내 다른 스레드들이 처리하게 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/za1Yf/btqEoUGXR6J/ytyd4a7jPobWiZW76Yy7N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/za1Yf/btqEoUGXR6J/ytyd4a7jPobWiZW76Yy7N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/za1Yf/btqEoUGXR6J/ytyd4a7jPobWiZW76Yy7N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fza1Yf%2FbtqEoUGXR6J%2Fytyd4a7jPobWiZW76Yy7N0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;여기까지가 실제로 Parallel Marking의 과정이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JtMLJ/btqEmvowlWf/xIaNIkDF6ds5ExqpQUJFq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JtMLJ/btqEmvowlWf/xIaNIkDF6ds5ExqpQUJFq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JtMLJ/btqEmvowlWf/xIaNIkDF6ds5ExqpQUJFq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJtMLJ%2FbtqEmvowlWf%2FxIaNIkDF6ds5ExqpQUJFq1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Concurrent Marking&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;여기까지 구현한다면, 더 좋은 방법에 대해 생각해보게 된다.&lt;/p&gt;
&lt;p&gt;&quot;혹시 어플리케이션 스레드를 동작시키면서 worker 스레드에서만 GC를 처리하는 방법은 없을까?&quot;&lt;/p&gt;
&lt;p&gt;이 질문을 해결하는 것이 Concurrent Marking이다. 자세한 구현 내용은 매우 어렵지만, Worker 스레드들이 Heap의 객체 그래프를 방문하는 동안 JS가 메인 스레드에서 실행될 수 있도록 해 주는 방법이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UMDeh/btqEnhJSCuH/CPN0kkakSRrdlTYjbiJX4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UMDeh/btqEnhJSCuH/CPN0kkakSRrdlTYjbiJX4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UMDeh/btqEnhJSCuH/CPN0kkakSRrdlTYjbiJX4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUMDeh%2FbtqEnhJSCuH%2FCPN0kkakSRrdlTYjbiJX4K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Background Marking 중에 발생하는 문제가 있다.&lt;/p&gt;
&lt;p&gt;- Heap 그래프가 수정되는 문제&lt;/p&gt;
&lt;p&gt;- Main 스레드와 Worker 스레드가 동시에 동일한 객체를 읽거나 수정함에 따라 Read/Write 레이스가 발생하는 문제&lt;/p&gt;
&lt;p&gt;이를 위한 객체 동기화 작업이 추가되어 오버헤드가 존재하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;V8의 Major GC의 Marking: Incremental + Concurrent + Parallel Marking을 종합&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhC8XG/btqEmowon6U/8FO0Giz21AP0jaPjjrefik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhC8XG/btqEmowon6U/8FO0Giz21AP0jaPjjrefik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhC8XG/btqEmowon6U/8FO0Giz21AP0jaPjjrefik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhC8XG%2FbtqEmowon6U%2F8FO0Giz21AP0jaPjjrefik%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;1. Old Space가 가득 차게 되면 Concurrent Marking을 진행하며, 그동안 JS가 실행된다. 이 때 발생하는 Heap 객체 그래프의 변경에 대해, 각 객체 간의 참조 및 색 정보를 Write Barrier로 기록하여, 추후 검은색 -&amp;gt; 흰색 객체일 경우에 대한 처리를 할 수 있도록 한다. Concurrent Marking이 완료되면 STW를 발생시키며, Main Thread는 다시 한 번 Heap 객체 그래프를 순회하여 모든 객체가 검은색인지 확인한다.&lt;/p&gt;
&lt;p&gt;2. 1번 작업이 끝나면 동시에 Sweep(Page에서 흰색 객체를 삭제해 Free-List에 등록)과 Compaction(메모리 단편화가 심한 Page를 골라 여유로운/새로운 페이지에 정리) 과정을 별개로 진행하며, 그렇기 때문에 모든 Old Space에 대해 Compaction이 일어나는 것은 아니게 된다. 하지만 이는 GC 시간을 줄이기 위함이며, 그렇기 때문에 다음 Major GC에서의 Compaction 과정에서 처리된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p&gt;Concurrent Marking에 대한 세부적인 정보가 많지 않아 어느 정도 두루뭉술하게 작성된 부분이 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;JVM의 GC와 동작이 비슷하다는 얘기를 많이 들었고, 마침 JVM GC에 대한 문서가 많아 이를 참고하면 될 것이라 생각했지만, 실제로는 다른 부분이 너무나도 많았네요.&lt;/p&gt;
&lt;p&gt;틀린 부분이 있다면 댓글을 남겨주시면 감사하겠습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;출처&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://speakerdeck.com/deepu105/v8-minor-gc&quot;&gt;https://speakerdeck.com/deepu105/v8-minor-gc&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&quot;&gt;https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://ui.toast.com/weekly-pick/ko_20200228/&quot;&gt;https://ui.toast.com/weekly-pick/ko_20200228/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://tech.ssut.me/concurrent-marking-in-v8/&quot;&gt;https://tech.ssut.me/concurrent-marking-in-v8/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://ygnoh.github.io/translation/javascript/2018/06/17/concurrent-marking-in-v8.html&quot;&gt;https://ygnoh.github.io/translation/javascript/2018/06/17/concurrent-marking-in-v8.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://deepu.tech/memory-management-in-v8/&quot;&gt;https://deepu.tech/memory-management-in-v8/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Web/Node.js</category>
      <author>Binceline</author>
      <guid isPermaLink="true">https://teraphonia.tistory.com/800</guid>
      <comments>https://teraphonia.tistory.com/800#entry800comment</comments>
      <pubDate>Sun, 24 May 2020 20:22:20 +0900</pubDate>
    </item>
  </channel>
</rss>